내가 원하는 기능을 정리해보자.
1. 녹화 전에는 녹화 중지버튼 비활성화, 녹화 중에는 녹화 시작버튼 비활성화.
2. 원하는 디렉토리에 영상 저장 가능.
3. 원하는 파일명으로 파일 저장 가능.
4. 이 모든 것을 버튼과 창으로 이루어진 인터페이스에 담기.
일단 구글링을 통해서 화면 녹화 함수를 정의해보았다. 그거를 입맛에 맞게 바꿔서 원하는 기능을 수행할 수 있도록 만들어보면
요런 게 될거다. opencv를 빠삭하게 이해하고 있는 게 아니라서, 다른 사람들이 만들어놓은 코드를 보고
수정할 것만 수정했다.
참고로 pip install opencv-python을 터미널에 입력해서 opencv를 다운받을 수 있다.
pyautogui랑 numpy는 다운받지 않고 import만 해주면 되고.
이제 우리는 이것을 함수에 넣고 버튼을 눌렀을 때 기능을 수행하도록 해주면 된다.
일단 이 모든 내용을 record()함수로 정의한다고 하자.
이렇게 우리가 만들어둔 레이아웃 파일에 넣어주자.
그 전에, 우리가 mainloop함수를 통해서 루프를 돌리고 있다는 것을 잊으면 안된다.
단순히 버튼의 커맨드에 이 함수 작동을 넣어버리면,
mainloop의 루프와 record함수 안의 while 루프가 충돌해서 정상적으로 실행이 되지 않는다.
그래서 우리는 threading이라는 패키지를 import해서 이용할 셈이다. 이렇게 해줘야 스레드가 충돌하지 않는다고..
일단 함수를 하나 더 정의해주자. 저 함수를 스레딩을 통해서 실행시켜 줄 함수다.
하나의 함수로 실행 중지 모두 수행할 수 있도록 정의하자.
어떻게 할 거냐면, recording이라는 변수를 녹화 중인지 아닌지에 따라서 참, 거짓값을 가지도록 할 생각이다.
일단 처음엔 녹화중이 아니니 recording = False로 두고 함수를 정의하면
이렇게 된다. 급발진이지만.. 주석은 충실히 달았으니 봐주세요 ㅠ
특정 상황에서 메세지 박스를 띄우기 위해서 from tkinter import messagebox as msgbox를 해줬고
특정 키를 누르는 효과를 넣기 위해서 import keyboard를 해줬다.
recording이 참일 때, 스레딩을 이용해서 우리가 아까 정의한 화면 녹화를 실행하겠다는 계획이다.
그러면 시작-중지 버튼에는 저 커맨드만 넣어주면 된다는 말이다.
그런데 지금 커맨드를 넣어도 실행이 원활히 되지 않을 것이다. 왜냐하면 엔트리 관련 설정에 구멍이 있기 때문.
파일 이름 엔트리야, 우리가 그냥 입력하고 get()함수를 써서 가져오면 되지만,
저장 경로는 고르는 것도, 고른 값을 엔트리에 집어넣는 것도 정의하지 않았기 때문.
그렇기에 따로 함수를 지정해줘야 한다. 디렉토리 관련 기능을 위해 tkinter의 filedialog 모듈과,
파일 위치 등의 처리를 위해 os를 import 해주겠다.
이렇게 정의를 해주자. 그러면 이제 모든 기능을 다 만들었으니 버튼들에 할당만 하면 된다.
이렇게 시작/중지 버튼과, 찾아보기.. 버튼에 command=를 이용해서 정의한 함수를 넣어주었다.
끝이다. 실행시켜 보면 의도한 대로 작동한다.
import pyautogui
import cv2
import numpy as np
from tkinter import*
from tkinter import messagebox
from threading import Thread
from tkinter import filedialog
record = False
run = False
#저장경로 함수
def search_save():
folder_selected = filedialog.askdirectory(initialdir=)#디폴트 위치 넣어주기
if folder_selected == '':
return
else:
save_dest.delete(0,END)
save_dest.insert(0, folder_selected)
#녹화 함수
def screenrec():
global file_name, save_dest
resolution = (1440,900) #화면 해상도
codec = cv2.VideoWriter_fourcc(*'MJPG') #비디오 코덱 설정
filename = '{}.avi'.format(file_name.get()) #타이핑한 파일 이름 가져오기
location = save_dest.get() #입력한 저장경로 가져오기
fps = 10.0
out = cv2.VideoWriter(location+'/'+filename, codec, fps, resolution) #결과물
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
if run == False:
break
out.release()
cv2.destroyAllWindows()
def startrec():
global record, run, file_name_frame
record = not record
if save_dest.get() == '' :
messagebox.showerror('경고', '저장 경로를 지정해 주십시오.')
record = not record
elif file_name.get() == '' :
messagebox.showerror('경고', '파일명을 지정해 주십시오.')
record = not record
elif record :
run = True
file_name.config(state='disabled')
messagebox.showinfo('알림', '녹화를 시작합니다.')
btn_stop.config(state='normal')
btn_start.config(state='disabled')
thread = Thread(target=screenrec)
thread.setDaemon(True)
thread.start()
elif record == False:
run = False
file_name.config(state='normal')
messagebox.showinfo('알림','녹화가 완료되었습니다.')
btn_start.config(state='normal')
btn_stop.config(state='disabled')
root = Tk()
root.resizable(False, False)
root.title('녹화 프로그램')
#녹화 프레임
btnframe = LabelFrame(root, text='녹화')
btnframe.pack(fill='x', padx=5, pady=5)
btn_start = Button(btnframe, text='녹화 시작', width=10, command=startrec)
btn_start.pack(expand = True ,side='left', padx=5, pady=5)
btn_stop = Button(btnframe, text='녹화 중지', width=10, command=startrec, state='disabled')
btn_stop.pack(expand = True, side='right', padx=5, pady=5)
#저장경로 프레임
save_dest_frame = LabelFrame(root, text='저장경로')
save_dest_frame.pack(fill='x', padx=5, pady=5)
save_dest = Entry(save_dest_frame)
save_dest.pack(side='right', fill='x', padx=5,pady=10, expand=True, ipady=5)
btn_save = Button(save_dest_frame, text='찾아보기', command=search_save)
btn_save.pack(side='left', padx=5, pady=5)
#파일 이름 프레임
file_name_frame = LabelFrame(root, text='파일명')
file_name_frame.pack(fill='x', padx=5, pady=5)
file_name = Entry(file_name_frame)
file_name.pack(fill='x', ipady=5, padx=5, pady=10)
root.mainloop()
전체 코드는 이렇게 된다 !
참고로 듀얼 모니터도 해상도를 맞게 설정해주면 된다고 피드백을 주신 분이 있으시니, 듀얼 모니터도 녹화 가능한 듯.
공백에 주석까지 100줄짜리 코드인데, 포스팅할 양도 많고 생각보다 어렵다.
그냥 내가 하는 프로젝트 정리라고는 하지만.. 다 쌩가고 완성품만 올릴 수도 없는 노릇이고.
하여튼 이렇게 또 하나 끝 !
윈도우즈에서는 잘 돌아갔지만, 맥에서는 저장한 영상이 실행되지 않는다.
opencv 공식 문서의 테스트 코드를 받아서 해봐도 안 되는걸로 보아
개인적인 문제는 아닌 듯. 추후 소식을 기다려봐야 될 것 같다.
다시 손보는 겸, 저장공간 엔트리에 입력을 제한하는 기능을 넣었다. 그리고 원래는 키보드 q를 프로그램으로 눌러서 녹화를 종료시켰는데, 혹시라도 이용자가 q를 누르면 종료될 위험이 있어서 별도의 변수를 만들어 정리했다.
누군가에게 조그만 도움이라도 되었으면 한다.
from tkinter import *
import cv2
import pyautogui
import numpy as np
import threading
from tkinter import messagebox as msgbox
from tkinter import filedialog
#함수 정의 1.녹화 함수
run = False #버튼 누르면 종료 명령
def record():
global file_name_entry, save_dir_entry
resolution = (1920,1080) #화면 해상도
codec = cv2.VideoWriter_fourcc(*'XVID') #비디오 코덱 설정
filename = '{}.avi'.format(file_name_entry.get()) #타이핑한 파일 이름 가져오기
location = save_dir_entry.get() #입력한 저장경로 가져오기
fps = 10.0
out = cv2.VideoWriter(location+'/'+filename, codec, fps, resolution) #결과물
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
if run == False: #run 변수가 거짓이 되면 프로그램 종료
break
out.release()
cv2.destroyAllWindows()
recording = False #시작에는 녹화 중이 아님
#함수 정의 2: 버튼 누르면 작동할 함수
def record_startstop(): #녹화상태를 바꿔주고, 녹화상태면 스레드를 실행시켜주는 함수
global recording, run
recording = not recording #실행시키면 recording 상태가 변해야 함
if save_dir_entry.get() == '':
msgbox.showerror('경고','저장 경로를 설정해주세요.') #저장경로 엔트리가 비었으면 에러메세지
recording = not recording #녹화 상태는 변해서는 안 되니깐
elif file_name_entry.get() == '':
msgbox.showerror('경고', '파일 이름을 저장해주세요.') #파일이름 엔트리가 비었으면 에러메세지
elif recording:
run = True
msgbox.showinfo('알림', '녹화를 시작합니다.')
btn_stop.config(state='active') #녹화 중지버튼 활성화
btn_start.config(state='disabled') #녹화 시작버튼 비활성화
thread = threading.Thread(target=record) #스레드 설정부터 시작
thread.setDaemon(True)
thread.start()
elif recording==False:
run = False #프로그램 종료를 위한 값
btn_start.config(state='active') #이때만 저장공간 엔트리는 작동 가능하게 바꾸기
btn_stop.config(state='disabled')
msgbox.showinfo('알림', '녹화가 완료되었습니다.')
def save_dir():
folder_selected = filedialog.askdirectory() #디렉토리 찾는 창 띄우기
if folder_selected == '':
return #선택된 폴더가 없으면 돌아가기
save_dir_entry.config(state='normal')
save_dir_entry.delete(0,END) #저장경로 엔트리 우선 비우고
save_dir_entry.insert(0, folder_selected) #선택된 폴더 주소를 삽입
save_dir_entry.config(state='normal')
#기본적인 화면 세팅
root = Tk()
root.title('화면 녹화')
root.resizable(False, False)
#녹화 시작, 중지 버튼 프레임
btn_frame = Frame(root)
btn_frame.pack(fill='x', pady=5, padx=5)
#녹화 시작, 중지 버튼
btn_start = Button(btn_frame, text='녹화 시작', width=10, command=record_startstop)
btn_start.pack(side='left', padx=5, pady=5, expand=True)
btn_stop = Button(btn_frame, text='녹화 중지',width=10, command=record_startstop)
btn_stop.pack(side='right', padx=5, pady=5, expand=True)
#저장 경로 프레임
save_dir_frame = LabelFrame(root, text='저장 경로')
save_dir_frame.pack(padx=5, pady=5, fill='x')
#저장경로 찾아보기 버튼
btn_save_dir = Button(save_dir_frame, text='찾아보기..', width=10, command=save_dir)
btn_save_dir.pack(padx=5, pady=5, side='left')
#저장경로 엔트리
save_dir_entry = Entry(save_dir_frame, state='readonly') #저장공간 입력 불가능하게 만들기
save_dir_entry.pack(side='right', fill='x', padx=5, pady=5)
#파일명 프레임
file_name_frame = Frame(root)
file_name_frame.pack(padx=5, pady=5, fill='x')
#파일명 입력창과 설명칸
file_name_label = Label(file_name_frame, text= '저장할 파일명 : ')
file_name_label.pack(side='left')
file_name_entry = Entry(file_name_frame)
file_name_entry.pack(padx=5,pady=5, fill='x')
root.mainloop()
'Programming > Projects' 카테고리의 다른 글
네이버 지도를 이용한 카페 데이터 추출 프로그램 (0) | 2022.06.20 |
---|---|
[Javascript] 자바스크립트로 아이스하키 경기 슈팅 기록 사이트 만들기 (0) | 2022.06.07 |
[Python] 화면 녹화 프로그램 1.-opencv, tkinter (0) | 2021.09.30 |
[Python] 계산기 만들기 - tkinter (0) | 2021.09.30 |
[Python] 인스타그램 좋아요 매크로 업그레이드- opencv (0) | 2021.09.30 |
댓글