Tkinter做一個本地視訊播放器(2)——彈幕
阿新 • • 發佈:2022-05-11
前文我們已經完成了一個集暫停、倍速、顯示進度條功能為一體的視訊播放器,今天我們再來增加一個新的功能——傳送彈幕。
tkinter播放視訊的原理,就是讀取每一幀的圖片,然後重新整理畫布。所以如果想實現彈幕功能,只要獲取輸入文字框中的文字,再寫到圖片上就好了。cv2.putText能夠實現這個功能,但無法新增中文,所以我們需要換一種方法。
首先,我們需要from PIL import ImageDraw,ImageFont
然後用ImageDraw.Draw轉換圖片格式,轉換後就可以呼叫.text()往圖片上寫中文了。
具體實現過程,只需要修改前文的一個函式:
def tkImage(n): global nowt,pointx,txt #倍速在這裡實現 for i in range(n): ref,frame = vc1.read() pilImage = Image.fromarray(frame) draw = ImageDraw.Draw(pilImage) font = ImageFont.truetype("simhei.ttf", 40, encoding="utf-8") #引數1:字型檔案路徑,引數2:字型大小 #txt是讀取的Entry內容,在button關聯的函式中獲取 if txt!="": draw.text((pointx, pointy), txt, (255, 255, 255), font=font) #pointx, pointy是文字新增的位置 pointx-=10 #每次向左移動10個單位 if(pointx==0): pointx=size[1] txt="" pilImage = cv2.cvtColor(np.array(pilImage), cv2.COLOR_BGR2RGB) pilImage = Image.fromarray(pilImage) pilImage = pilImage.resize((window_width, window_height),Image.ANTIALIAS) tkImage = ImageTk.PhotoImage(image=pilImage) nowt+=n #記錄當前的幀數 return tkImage
現在我們把前文的顯示四張影象改成一張,重新整理一下程式碼:
import time import tkinter as tk from tkinter import * from tkinter import ttk import cv2 from PIL import Image, ImageTk,ImageFilter,ImageDraw,ImageFont import multiprocessing import numpy as np import random import os filePath = 'D:\\movie\\' #電影存放路徑 mlist=os.listdir(filePath) #資料夾下所有檔名稱 window_width=960 #介面寬 window_height=760 #介面長 image_width=int(window_width*0.5) #影象寬 image_height=int(window_height*0.5) #影象長 imagepos_x=0 #畫布位置x imagepos_y=0 #畫布位置Y lock=0 #暫停標誌 n=1 #初始倍速 nowt=0 #當前幀數 nows=0 #當前播放時長(秒) pointx=0 #彈幕起始點橫座標 pointy=0 #彈幕起始點縱座標 txt="" #彈幕內容 #獲取當前影象 def tkImage(n): #倍速在這裡實現 global nowt,pointx,pointy,txt for i in range(n): ref,frame = vc1.read() pilImage = Image.fromarray(frame) draw = ImageDraw.Draw(pilImage) font = ImageFont.truetype("simhei.ttf", 40, encoding="utf-8")#引數1:字型檔案路徑,引數2:字型大小 if txt!="": draw.text((pointx, pointy), txt, (255, 255, 255), font=font) pointx-=10 if(pointx==-50): pointx=size[1] txt="" pilImage = cv2.cvtColor(np.array(pilImage), cv2.COLOR_BGR2RGB) pilImage = Image.fromarray(pilImage) pilImage = pilImage.resize((window_width, 720),Image.ANTIALIAS) tkImage1 = ImageTk.PhotoImage(image=pilImage) nowt+=n return tkImage1 #影象的顯示與更新 def video(): global nows,nowt def video_loop(): global nows,nowt try: while True: if lock % 2 == 0: #是否暫停 picture1=tkImage(n) if nowt >= fps: nowt=0 #每過一秒則清零重計 nows+=1 #每過一秒,當前播放時間也加1 mi=str(int(nows/60)) #分鐘 se=str(int(nows%60)) #秒 if int(mi)<10: mi="0"+mi if int(se)<10: se="0"+se showt=mi+":"+se #當前時間 show=showt+"/"+totalt #最終顯示格式(當前時間/總時長) tlabel.config(text=show) canvas.coords(fill_line, (0, 0, int(window_width/tt*nows), 15)) #填充進度條 canvas1.create_image(0,0,anchor='nw',image=picture1) win.update_idletasks() win.update() else: win.update_idletasks() win.update() except: return #每次開始播放前初始化 nows,nowt=0,0 canvas.coords(fill_line, (0, 0, 0, 15)) video_loop() vc1.release() cv2.destroyAllWindows() #右鍵事件:倍速 def right(self): global n n+=1 if n>4: n=1 #左鍵事件:暫停 def left(self): global lock lock+=1 #按鈕事件:獲取彈幕 def get_txt(): global txt,pointx,pointy txt=E1.get() pointx=size[1] #初始x座標 pointy=random.randint(50,150) #初始y座標隨機 E1.delete(0, END) #播放選中檔案 def start(): global vc1,size,frames_num,fps,totalt,tt,pointx val = theLB.get() #獲得下拉框當前內容 vc1 = cv2.VideoCapture(filePath+val) #讀取視訊 size = (int(vc1.get(cv2.CAP_PROP_FRAME_HEIGHT)), int(vc1.get(cv2.CAP_PROP_FRAME_WIDTH))) #視訊影象的長和寬 frames_num=vc1.get(cv2.CAP_PROP_FRAME_COUNT) #總幀數 fps = vc1.get(cv2.CAP_PROP_FPS) #幀率 totalt=str(int(frames_num/fps/60))+":"+str(int(frames_num/fps)%60) #視訊時長 tt=int(frames_num/fps) #進度條疊滿所需次數 pointx=size[1] #彈幕起始座標x video() #跳轉到指定位置 def kj(): global nows,vc1 time=int(int(s1.get())*frames_num/fps/100) #要跳轉到的時長 vc1.set(cv2.CAP_PROP_POS_FRAMES,int(time*fps)) #讀取到指定幀數 ref,frame = vc1.read() nows=time '''佈局''' win = tk.Tk() win.geometry(str(window_width+120)+'x'+str(window_height+20)) #顯示視訊的畫布 canvas1 =Canvas(win,bg='white',width=window_width,height=720) canvas1.place(x=imagepos_x,y=imagepos_y) canvas1.bind('<Button-1>', left) canvas1.bind('<Button-3>', right) #顯示進度條的畫布 canvas = Canvas(win, width=image_width*2, height=15, bg="white") canvas.place(x=0, y=722) fill_line=canvas.create_rectangle(0,0,0,15,fill = 'LightGreen') #座標是相對於畫布的 #彈幕輸入框 E1 = Entry(win, bd =5,width=100) E1.place(x=0, y=745) #傳送彈幕按鈕 B1 = Button(win, text="傳送", command=get_txt,font=('黑體', 10),fg='blue',width=10,height=2) B1.place(x=750, y=742) #顯示時間的Label tlabel = Label(win,font=('黑體', 13),text='') tlabel.place(x=850, y=750) #選擇影片下拉框 theLB = ttk.Combobox(win,width=12,height=10) theLB["values"] = mlist theLB.current(0) #預設選第一個 theLB.place(x=965, y=0) #播放影片按鈕 B2 = Button(win, text="播放", command=start,font=('黑體', 10),fg='red',width=10,height=2) B2.place(x=980, y=50) #跳轉按鈕 B3 = Button(win, text="跳轉", command=kj,font=('黑體', 10),fg='red',width=10,height=2) B3.place(x=980, y=150) #選擇跳轉位置的滾動條 s1 = Scale(win,from_=0,to=99,orient=HORIZONTAL) #orient=HORIZONTAL設定水平方向顯示 s1.place(x=970, y=100) win.mainloop()
上述程式碼不僅實現了我們所說的功能,我還用下拉框Combobox自動載入資料夾下的所有檔名,以供選擇檔案播放;並且能夠拖動滾動條跳轉到指定位置,利用了cv2.VideoCapture的.set()方法。
怎麼樣,更像一個視訊播放器了吧?如果你還想往下做,可以加入“新增到播放列表”的功能,選擇其他路徑下的檔案新增到全域性變數mlist裡,然後更新一下Combobox的values即可。