-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.py
258 lines (216 loc) · 7.6 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
from tkinter import Tk, Label, NE, Frame, LabelFrame, W, E, N, S, HORIZONTAL, StringVar, filedialog, messagebox, PhotoImage
from tkinter.ttk import Progressbar, Button, OptionMenu, Label
from video_utils import bulk_validate_video, bulk_video_converter
import os
import time
import _thread
# TODO: Add Logging
# TODO: Add Cancel/Abort Button
# TODO: Add Check for feasibility of video speed
# TODO: Add a more elegant solution for multithreading
class TimelapseGUI():
# CLASS CONSTANTS
READY_TEXT = "Ready!"
ICON_NAME = "assets/favicon.png"
MIN_WIDTH = 500
MIN_HEIGHT = 300
CHOICES = [
'Choose Speed',
'2x',
'5x',
'10x',
'20x',
'30x',
'50x',
'100x',
'200x',
'300x',
'500x',
'1000x',
]
SUPPORTED_FORMATS = [
'.mp4',
'.webm',
'.mpg',
'.avi',
'.mov',
'.m4v',
'.flv',
'.mkv'
]
def __init__(self):
"""
__init__
Initializes the GUI elements
"""
# variables
self.files = None
self.file_status = None
self.var_open_files = None
self.progress = None
# Root Window Properties
self.root = Tk()
self.root.title("Timelapse Creator")
self.root.minsize(self.MIN_WIDTH, self.MIN_HEIGHT)
# This part is to make sure that the program runs with or without n icon file.
try:
if os.path.exists(self.ICON_NAME):
self.icon = PhotoImage(file=self.ICON_NAME)
self.root.iconphoto(False, self.icon)
elif os.path.exists(os.path.split(self.ICON_NAME)[-1]):
self.icon = PhotoImage(file=os.path.split(self.ICON_NAME)[-1])
self.root.iconphoto(False, self.icon)
except Exception as e:
print(f"Could not load Icon due to Error: {e}")
# Buttons and Widgets
self.config_frames()
self.config_buttons()
self.config_progress_bar()
self.config_label()
def config_frames(self):
"""
config_frames
Set up the different sections of the GUI window
"""
# BUTTON SPACE
self.buttons_frame = Frame(self.root)
self.buttons_frame.grid(row=0, column=0, columnspan=3,
padx=10, pady=10, sticky=E+W)
# PROGRESS BAR SPACE
self.progress_frame = Frame(self.root)
self.progress_frame.grid(row=1, column=0, columnspan=3,
padx=10, pady=10, sticky=E+W)
# Configure how many rows and columns are there in and progress_frame
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(1, weight=1)
self.progress_frame.columnconfigure(0, weight=1)
self.progress_frame.rowconfigure(0, weight=1)
def config_buttons(self):
"""
config_buttons
Define the interactive Buttons used for the GUI App
"""
# Open File Browser Button
self.btn_open_files = Button(
self.buttons_frame,
command=self.browseFiles,
text='Select Videos'
)
self.btn_open_files.grid(row=0, column=0, padx=(10), pady=10)
# Dropdown Selector button
self.var_open_files = StringVar(self.buttons_frame)
self.dropdown_button = OptionMenu(
self.buttons_frame, self.var_open_files, *self.CHOICES)
self.dropdown_button.grid(row=0, column=1, padx=(10), pady=10)
# Convert Button
self.btn_convert = Button(self.buttons_frame,
text='Convert', command=self.convert_video)
self.btn_convert.grid(row=0, column=2, padx=(10), pady=10)
def config_progress_bar(self):
"""
config_progress_bar
Configure the Progress bar
"""
self.progress = Progressbar(self.progress_frame, orient=HORIZONTAL,
length=100, mode='determinate')
self.progress.grid(row=1, column=0, sticky=E+W)
def config_label(self):
"""
config_label
Add the Dynamic labels for progress tracking
"""
self.file_status = Label(
self.progress_frame,
text=self.READY_TEXT
)
self.file_status.grid(row=0, column=0, padx=(10), pady=10)
self.file_status_percent = Label(
self.progress_frame,
text="0%"
)
self.file_status_percent.grid(row=1, column=0, padx=(0), pady=0)
def run(self):
"""
run
Run the GUI Loop.
"""
self.root.mainloop()
def file_format_generator(self, formats: list):
"""
file_format_generator Generates the required file format - helper function
Takes a List of strings as input, adds their upper case versions as well
Converts them to a space separated string, and returns the same
Args:
formats (list): comma separated strings of file extensions in lower case
Returns:
string: space separated file extensions along with corresponding upper case format
"""
formats = formats + [value.upper() for value in formats]
return " ".join(formats)
def browseFiles(self):
"""
browseFiles
Creates the File Selector dialogbox
Returns:
boolean: True if valid file is selected, False if invalid
"""
self.files = filedialog.askopenfilenames(
# initialdir="/media/Data/Downloads/",
title="Videos",
filetypes=(
("Video Files", self.file_format_generator(self.SUPPORTED_FORMATS)),
)
)
# Check file validity
valid = bulk_validate_video(self.files)
if not valid:
messagebox.showerror(
title="Invalid File",
message="The File that you entered is invalid. Please retry!"
)
return False
return True
def convert_video(self):
"""
convert_video Main video converter function
Uses OpenCV to read and write the video
While skipping frames based on the given input
Returns:
boolean: Returns True or False based on success or failure
"""
# Warning Box for if proper files have not been selected
if not self.files:
messagebox.showwarning(
title="File Not Selected",
message="You have not selected any file. Please Click on \"Select Videos\""
)
return False
# Extract the multiplier number
fps_multiplier = self.var_open_files.get()
if not fps_multiplier:
return False
# If invalid multiplier selected, raise warning
try:
fps_multiplier = int(fps_multiplier[:-1])
except Exception as e:
messagebox.showwarning(
title="Select Speed",
message="Please Select Speed of the video from the dropdown menu"
)
return True
# Disable the button during converstion to avoid multiple inputs
self.btn_convert["state"] = "disabled"
# Start the main conversion in a different thread to avoid UI freeze
_thread.start_new_thread(bulk_video_converter, (
self.files,
fps_multiplier,
None,
self.file_status,
self.file_status_percent,
self.progress,
self.root,
self.btn_convert
))
if __name__ == '__main__':
gui = TimelapseGUI()
gui.run()