python播放器教程:会个python就可以这么任性吗
python播放器教程:会个python就可以这么任性吗
以下为主程序VE.py源码:
# -*- coding:utf-8 -*-
from PyQt5.Qt import pyqtSignal QApplication QFileDialog QMutex QSlider
from PyQt5.uic import loadUi
from PyQt5.QtCore import pyqtSignal QObject QTimer QThread
from PyQt5.QtGui import QIcon QPixmap QColor
from pysubs2 import SSAFile SSAEvent make_time
import win32com.client as wincl
import os platform threading sys time wave re pythoncom glob
# 设置VLC库路径,需在import vlc之前
if len(sys.argv) > 1:
os.chdir((sys.argv[0]).replace((sys.argv[0].split('\\'))[-1] ''))
os.add_dll_directory(os.getcwd())
import vlc
ERROR = ''
qmut_1 = QMutex() # 创建线程锁
class Sound(QThread): # 线程1
def __init__(self text):
self.text = text
super().__init__()
def run(self):
qmut_1.lock() # 加锁
pythoncom.CoInitialize()
(wincl.Dispatch("SAPI.SpVoice")).Speak(self.text)
pythoncom.CoUninitialize()
qmut_1.unlock() # 解锁.
class Player:
'''
args:设置 options
'''
def __init__(self *args):
def startplay():
if args:
instance = vlc.Instance(*args)
self.media = instance.media_player_new()
else:
self.media = vlc.MediaPlayer()
thread = threading.Thread(target=startplay())
thread.start()
# 设置待播放的url地址或本地文件路径,每次调用都会重新加载资源
def set_uri(self uri):
self.media.set_mrl(uri)
# 播放 成功返回0,失败返回-1
def play(self path=None):
if path:
self.set_uri(path)
return self.media.play()
else:
return self.media.play()
# 暂停
def pause(self):
self.media.pause()
# 恢复
def resume(self):
self.media.set_pause(0)
# 停止
def stop(self):
self.media.stop()
# 释放资源
def release(self):
return self.media.release()
# 是否正在播放
def is_playing(self):
return self.media.is_playing()
# 已播放时间,返回毫秒值
def get_time(self):
return self.media.get_time()
# 拖动指定的毫秒值处播放。成功返回0,失败返回-1 (需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
def set_time(self ms):
return self.media.set_time(ms)
# 音视频总长度,返回毫秒值
def get_length(self):
return self.media.get_length()
# 获取当前音量(0~100)
def get_volume(self):
return self.media.audio_get_volume()
# 设置音量(0~100)
def set_volume(self volume):
return self.media.audio_set_volume(volume)
# 返回当前状态:正在播放;暂停中;其他
def get_state(self):
state = self.media.get_state()
if state == vlc.State.Playing:
return 1
elif state == vlc.State.Paused:
return 0
else:
return -1
# 当前播放进度情况。返回0.0~1.0之间的浮点数
def get_position(self):
return self.media.get_position()
# 拖动当前进度,传入0.0~1.0之间的浮点数(需要注意,只有当前多媒体格式或流媒体协议支持才会生效)
def set_position(self float_val):
return self.media.set_position(float_val)
# 获取当前文件播放速率
def get_rate(self):
return self.media.get_rate()
# 设置播放速率(如:1.2,表示加速1.2倍播放)
def set_rate(self rate):
return self.media.set_rate(rate)
# 设置宽高比率(如"16:9" "4:3")
def set_ratio(self ratio):
self.media.video_set_scale(0) # 必须设置为0,否则无法修改屏幕宽高
self.media.video_set_aspect_ratio(ratio)
# 设置窗口句柄
def set_window(self wm_id):
if platform.system() == 'Windows':
self.media.set_hwnd(wm_id)
else:
self.media.set_xwindow(wm_id)
# 注册监听器
def add_callback(self event_type callback):
self.media.event_manager().event_attach(event_type callback)
print('这里是监听器')
Stats().ui.videotime.setMaximum(self.media.get_length())
Stats().ui.videotime.setValue(self.media.get_time())
# 移除监听器
def remove_callback(self event_type callback):
self.media.event_manager().event_detach(event_type callback)
class Stats:
def __init__(self):
# 从文件中加载UI定义 从 UI 定义中动态 创建一个相应的窗口对象 注意:里面的控件对象也成为窗口对象的属性了 比如 self.ui.button self.ui.textEdit
self.ui = loadUi('plugins\\UI\\main.ui')
self.ui.setWindowIcon(QIcon('plugins\\UI\\logo.png'))
self.path = ''
self.loging = ''
self.Enable = True
self.player = Player()
self.player.set_window(self.ui.frame.winId())
self.tw = timework()
self.tw.videotime.connect(self.videotime)
self.ui.answer.setText('朗读已启用')
self.ui.cmd.returnPressed.connect(self.playButton)
self.ui.openButton.clicked.connect(self.openFileDialog) #打开
self.ui.backButton.clicked.connect(self.backButton) #后退
self.ui.playButton.clicked.connect(self.playButton) #播放/暂停
self.ui.flowordButton.clicked.connect(self.flowordButton) #前进
# self.ui.stopButton.setIcon(QIcon('UI\stop.png')) #停止
self.ui.stopButton.clicked.connect(self.stopButton) #停止
self.ui.sound.valueChanged.connect(self.sound) #声音
self.ui.videotime.sliderMoved.connect(self.videotime) #拖动进度条同步刷新画面
# self.ui.videotime.sliderReleased.connect(self.videotime) #拖动进度条完毕刷新界面
self.ui.frame.setStyleSheet("background-color: rgb(0 0 0);")#设置播放控件背景色
self.ui.answer.clicked.connect(self.victostr) # 回答问题
self.clock_time = 0
self.timer = QTimer() # 生成定时器
self.timer.start(200)
self.File = sys.argv
self.timetime = 0
self.skip = 0
self.timer.timeout.connect(self.clock) # 绑定计时函数 self.clock
def clock(self):
num = self.player.get_time() #获取播放进度
if num < 0 :
num = 0
if num > 0:
self.ui.videotime.setValue(num) #同步进度条
self.ui.minlcd.display(str(num//60000 100)[1:]) #显示分钟数
self.ui.slcd.display(':' str(num//1000` 100)[1:]) #显示秒数
self.playsubs(num)
def playsubs(self num):
for i in self.subs:
if (i.start) <= num <= (i.end):
sub = i.text.replace(r'\N' '\n')
# if not self.is_chinese(sub):
# sub = self.translate(sub)
if '\n' in sub:
sub = sub.split('\n')[0]
if self.Enable and self.loging != sub:
print(f'进度:{str(num)} 开始时间:{str(i.start)} 结束时间:{str(i.end)} 字幕:{sub}')
self.ui.echoanswer.setText(sub)
self.loging = sub
self.thread_1 = Sound(sub) # 创建线程
self.thread_1.start() # 开始线程
def victostr(self):
if self.Enable:
self.ui.answer.setText('朗读已禁用')
self.Enable = False
self.ui.echoanswer.setText('')
else:
self.ui.answer.setText('朗读已启用')
self.Enable = True
def backButton(self): #后退
self.player.set_time(self.player.get_time() - 5000)
self.ui.videotime.setValue(self.player.get_time())
def playButton(self): #播放/暂停
if self.path:
if self.player.is_playing() == 1 :
self.player.pause()
self.ui.playButton.setText('播放')
else:
self.player.resume()
self.ui.playButton.setText('暂停')
self.ui.videotime.setValue(self.player.get_time())
else:
self.openFileDialog()
def flowordButton(self): #前进
self.player.set_time(self.player.get_time() 5000)
self.ui.videotime.setValue(self.player.get_time())
def stopButton(self): #停止ian
self.player.stop()
self.ui.videotime.setValue(self.player.get_time())
def sound(self):
self.player.set_volume(self.ui.sound.value())
def videotime(self):
self.ui.cmd.setText('')
self.player.set_time(self.ui.videotime.value())
def initialization(self): #初始化 等待接收文件
pass
def openFile(self):#类型关联打开文件
self.getsubs(self.File[1])
def timehandle(self timestr):
if ':' in timestr:
stoptime =timestr.split(':')
second = float((stoptime)[0]) * 60 float((stoptime)[1]) # 获得暂停时间
else:
second = float(timestr)
return second
def openFileDialog(self):#打开文件
# 生成文件对话框对象
dialog = QFileDialog()
# 设置文件过滤器,这里是任何文件,包括目录噢
dialog.setFileMode(QFileDialog.AnyFile)
# 设置显示文件的模式,这里是详细模式
dialog.setViewMode(QFileDialog.Detail)
if dialog.exec_():
fileNames = dialog.selectedFiles()
self.getsubs(fileNames[0])
def getsubs(self path = ''):
self.path = path
self.ui.setWindowTitle(path)#将路径显示到标题栏上
self.player.play(path)
self.ui.playButton.setText('暂停')
time.sleep(0.1)
self.ui.sound.setValue(self.player.get_volume())
self.ui.videotime.setMaximum(self.player.get_length())
self.ui.videotime.setValue(self.player.get_time())
P = os.path.dirname(path)
if subpath := glob.glob(f'{P}/*.srt'):
self.subs = SSAFile.load(subpath[0])
elif subpath := glob.glob(f'{P}/*.ass'):
self.subs = SSAFile.load(subpath[0])
elif subpath := glob.glob(f'{P}/*.ssa'):
self.subs = SSAFile.load(subpath[0])
class timework(QObject):
videotime = pyqtSignal(QSlider int)
app = QApplication([])
stats = Stats()
stats.ui.show()
stats.initialization()
sys.exit(app.exec_())
main.ui源码:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>933</width>
<height>744</height>
</rect>
</property>
<property name="windowTitle">
<string>野子de播放器</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="echoanswer">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>18</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="videotime">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLCDNumber" name="minlcd">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="digitCount">
<number>3</number>
</property>
<property name="segmentStyle">
<enum>QLCDNumber::Flat</enum>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="slcd">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="digitCount">
<number>3</number>
</property>
<property name="segmentStyle">
<enum>QLCDNumber::Flat</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="cmd">
<property name="maximumSize">
<size>
<width>0</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="openButton">
<property name="text">
<string>打开</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="backButton">
<property name="text">
<string>后退</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="text">
<string>播放</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="answer">
<property name="text">
<string>朗读</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="flowordButton">
<property name="text">
<string>前进</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopButton">
<property name="text">
<string>停止</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="soundtitle">
<property name="text">
<string>音量</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sound">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
logo:
- 把main.ui的内容复制粘贴 保存到plugins\UI目录下
- 把VE.py的内容复制粘贴保存一下
- 在pycharm中安装python-vlc
- 从http://download.videolan.org/pub/videolan/vlc/下载对应版本的VLC Portable文件,放到plugins文件夹下
- 把logo另存为到plugins\UI目录下
- 然后pycharm直接运行就可以了
- 也可以使用pyinstaller打包成独立的exe文件