2023-09-30 10:12:06 +00:00
|
|
|
import cv2
|
|
|
|
import os
|
|
|
|
import numpy as np
|
|
|
|
import tkinter as tk
|
|
|
|
from tkinterdnd2 import DND_FILES, TkinterDnD
|
2023-10-01 13:10:23 +00:00
|
|
|
from tkinter import filedialog, simpledialog, messagebox, scrolledtext
|
2023-09-30 10:12:06 +00:00
|
|
|
from datetime import datetime
|
|
|
|
import sys
|
|
|
|
import webbrowser
|
|
|
|
import screeninfo
|
2023-10-01 13:10:23 +00:00
|
|
|
import feedparser
|
|
|
|
import subprocess
|
|
|
|
import platform
|
|
|
|
import urllib.request
|
|
|
|
import re
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
|
|
|
|
print(f"New start\n")
|
2023-09-30 10:12:06 +00:00
|
|
|
|
|
|
|
# Constants
|
2023-09-30 11:37:14 +00:00
|
|
|
v2cversion1 = 1
|
2023-10-01 13:10:23 +00:00
|
|
|
v2cversion2 = 1
|
2023-09-30 10:12:06 +00:00
|
|
|
v2cversion3 = 0
|
|
|
|
v2cversion = f"{v2cversion1}.{v2cversion2}.{v2cversion3}"
|
|
|
|
frame_step = 100
|
|
|
|
value_threshold = 11.0
|
|
|
|
SAVE = True
|
|
|
|
SHOW = True
|
|
|
|
processing = False
|
|
|
|
|
2023-10-01 13:10:23 +00:00
|
|
|
# Define the RSS feed URL
|
|
|
|
rss_feed_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases.rss"
|
|
|
|
|
2023-09-30 10:12:06 +00:00
|
|
|
# Create the root window
|
|
|
|
root = TkinterDnD.Tk()
|
|
|
|
root.title("Video2Crops")
|
2023-10-01 13:10:23 +00:00
|
|
|
root.geometry("300x450")
|
|
|
|
|
|
|
|
def open_changelog_window():
|
|
|
|
changelog_window = tk.Toplevel(root)
|
|
|
|
changelog_window.title("Video2Crops Changelog")
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Parse the RSS feed
|
|
|
|
feed = feedparser.parse(rss_feed_url)
|
|
|
|
|
|
|
|
if not feed.entries:
|
|
|
|
scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
|
|
|
|
scrolled_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
scrolled_text.insert(tk.END, "No updates available.")
|
|
|
|
return
|
|
|
|
|
|
|
|
# Create a single scrolled text widget to display all changelog entries
|
|
|
|
scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
|
|
|
|
scrolled_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
|
|
entry_num = 0
|
|
|
|
for entry in feed.entries:
|
|
|
|
entry_num += 1
|
|
|
|
version_str = extract_version(entry.link)
|
|
|
|
changelog_md = extract_changelog(entry.content[0].value)
|
|
|
|
|
|
|
|
if version_str is not None and changelog_md is not None:
|
|
|
|
version_info = f"# v{version_str}"
|
|
|
|
print(version_str)
|
|
|
|
if str(version_str) == str(v2cversion) and entry_num == 1:
|
|
|
|
version_info += "(up to date)"
|
|
|
|
elif str(version_str) == str(v2cversion):
|
|
|
|
version_info += "(current)"
|
|
|
|
elif entry_num == 1:
|
|
|
|
version_info += "(latest)"
|
|
|
|
version_info += "\n"
|
|
|
|
|
|
|
|
date_pattern = r'(\w{3}, \d{2} \w{3} \d{4})'
|
|
|
|
pub_date = re.search(date_pattern, entry.published)
|
|
|
|
if pub_date:
|
|
|
|
rel_date = pub_date.group(1)
|
|
|
|
|
|
|
|
version_info += f"\n{rel_date}\n\n"
|
|
|
|
|
|
|
|
# Append the version information and changelog entry to the scrolled text widget
|
|
|
|
changelog_text = version_info + changelog_md + f"\n--------------------\n"
|
|
|
|
scrolled_text.insert(tk.END, changelog_text)
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
scrolled_text = scrolledtext.ScrolledText(changelog_window, wrap=tk.WORD)
|
|
|
|
scrolled_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
scrolled_text.insert(tk.END, f"An error occurred: {str(e)}")
|
|
|
|
|
|
|
|
def extract_version(title):
|
|
|
|
# Use regular expressions to extract the version number from the title
|
|
|
|
match = re.search(r'(\d+\.\d+\.\d+)', title)
|
|
|
|
if match:
|
|
|
|
return match.group(1)
|
|
|
|
return None
|
|
|
|
|
|
|
|
def extract_changelog(description):
|
|
|
|
# Parse the HTML content using BeautifulSoup
|
|
|
|
soup = BeautifulSoup(description, 'html.parser')
|
|
|
|
|
|
|
|
# Find all changelog sections
|
|
|
|
changelog_sections = soup.find_all('h3', id='user-content-changelog')
|
|
|
|
|
|
|
|
changelogs = []
|
|
|
|
|
|
|
|
for changelog_section in changelog_sections:
|
|
|
|
# Extract the changelog text as plain text
|
|
|
|
changelog_text = changelog_section.find_next('ul').get_text()
|
|
|
|
changelogs.append(changelog_text.strip())
|
|
|
|
|
|
|
|
# Join the changelogs into a single string
|
|
|
|
changelog_str = '\n'.join(changelogs)
|
|
|
|
|
|
|
|
return changelog_str if changelog_str else None
|
|
|
|
|
|
|
|
# Function for updating Video2Crops
|
|
|
|
def check_for_updates():
|
|
|
|
try:
|
|
|
|
# Parse the RSS feed
|
|
|
|
feed = feedparser.parse(rss_feed_url)
|
|
|
|
|
|
|
|
if not feed.entries:
|
|
|
|
response = tk.messagebox.showinfo(
|
|
|
|
"No updates found",
|
|
|
|
f"Latest version: ({v2cversion})\n"
|
|
|
|
f"Current version: ({v2cversion})\n"
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
|
|
|
# Get the latest release
|
|
|
|
latest_release = feed.entries[0]
|
|
|
|
latest_version_str = extract_version(latest_release.link)
|
|
|
|
|
|
|
|
if latest_version_str is None:
|
|
|
|
tk.messagebox.showinfo(
|
|
|
|
"No update Available",
|
|
|
|
f"Latest version: ({v2cversion})\n"
|
|
|
|
f"Current version: ({v2cversion})\n"
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
|
|
|
print(f"RSS Version: {latest_version_str}")
|
|
|
|
|
|
|
|
# Extract version numbers from the latest release title
|
|
|
|
latest_version = tuple(map(int, latest_version_str.split('.')))
|
|
|
|
|
|
|
|
# Compare the versions
|
|
|
|
if latest_version > (v2cversion1, v2cversion2, v2cversion3):
|
|
|
|
# Prompt the user for an update
|
|
|
|
response = tk.messagebox.askyesno(
|
|
|
|
"Update Available",
|
|
|
|
f"A newer version ({latest_version_str}) is available.\n"
|
|
|
|
f"Current version: ({v2cversion})\n"
|
|
|
|
"Do you want to update?"
|
|
|
|
)
|
|
|
|
|
|
|
|
if response:
|
|
|
|
# Determine the platform
|
|
|
|
current_platform = platform.system().lower()
|
|
|
|
if current_platform == "windows":
|
|
|
|
# On Windows, download and run the installer
|
|
|
|
installer_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-windows-installer.exe".format(latest_version_str)
|
|
|
|
try:
|
|
|
|
urllib.request.urlretrieve(installer_url, "Video2Crops-windows-installer.exe")
|
|
|
|
subprocess.Popen("Video2Crops-windows-installer.exe", shell=True)
|
|
|
|
root.quit()
|
|
|
|
except:
|
|
|
|
tk.messagebox.showinfo(
|
|
|
|
"Installer not found",
|
|
|
|
f"The installer file was not found.\nPlease check your antivirus and network connectivity"
|
|
|
|
)
|
|
|
|
elif current_platform == "linux":
|
|
|
|
# On Linux, download and run the executable
|
|
|
|
executable_url = "https://git.rolfsvaag.no/frarol96/Video2Crops/releases/download/v{}/Video2Crops-linux".format(latest_version_str)
|
|
|
|
try:
|
|
|
|
urllib.request.urlretrieve(executable_url, "Video2Crops-linux")
|
|
|
|
os.chmod("Video2Crops-linux", 0o755)
|
|
|
|
subprocess.Popen("./Video2Crops-linux", shell=True)
|
|
|
|
root.quit()
|
|
|
|
except:
|
|
|
|
tk.messagebox.showinfo(
|
|
|
|
"Executable not found",
|
|
|
|
f"The executable file was not found.\nPlease check your antivirus and network connectivity"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
tk.messagebox.showinfo(
|
|
|
|
"No update Available",
|
|
|
|
f"Latest version: ({v2cversion})\n"
|
|
|
|
f"Current version: ({v2cversion})\n"
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
tk.messagebox.showerror("Error", f"An error occurred: {str(e)}")
|
2023-09-30 10:12:06 +00:00
|
|
|
|
|
|
|
# Function to create the output directory and return its path
|
|
|
|
def create_output_directory(video_filename):
|
|
|
|
base_directory = os.getcwd() # You can change this to specify a different base directory
|
|
|
|
video_filename_base, _ = os.path.splitext(os.path.basename(video_filename))
|
|
|
|
output_folder_path = os.path.join(base_directory, video_filename_base)
|
|
|
|
|
|
|
|
# Create the output directory if it doesn't exist
|
|
|
|
if not os.path.exists(output_folder_path):
|
|
|
|
os.makedirs(output_folder_path)
|
|
|
|
|
|
|
|
return output_folder_path
|
|
|
|
|
|
|
|
# Function to calculate ROI position based on alignment and size settings
|
|
|
|
def calculate_roi(video_width, video_height, vertical_alignment, horizontal_alignment, roi_size):
|
|
|
|
y1 = (video_height - roi_size) // 2
|
|
|
|
x1 = (video_width - roi_size) // 2
|
|
|
|
|
|
|
|
if vertical_alignment == "Top":
|
|
|
|
y1 = 0
|
|
|
|
elif vertical_alignment == "Center":
|
|
|
|
y1 = (video_height - roi_size) // 2
|
|
|
|
elif vertical_alignment == "Bottom":
|
|
|
|
y1 = video_height - roi_size
|
|
|
|
|
|
|
|
if horizontal_alignment == "Left":
|
|
|
|
x1 = 0
|
|
|
|
elif horizontal_alignment == "Center":
|
|
|
|
x1 = (video_width - roi_size) // 2
|
|
|
|
elif horizontal_alignment == "Right":
|
|
|
|
x1 = video_width - roi_size
|
|
|
|
|
|
|
|
x2 = x1 + roi_size
|
|
|
|
y2 = y1 + roi_size
|
|
|
|
|
|
|
|
return x1, y1, x2, y2
|
|
|
|
|
|
|
|
# Create a function to process video files
|
|
|
|
def process_video(video_filenames, main_window):
|
|
|
|
global processing
|
|
|
|
processing = True
|
|
|
|
|
|
|
|
def write_log(message):
|
|
|
|
logfile_path = os.path.join(output_folder, 'logfile.txt')
|
|
|
|
with open(logfile_path, 'a') as logfile:
|
|
|
|
log_message = f"{datetime.now().strftime('%m-%d_%H:%M:%S')} - {message}\n"
|
|
|
|
logfile.write(log_message)
|
|
|
|
print(log_message)
|
|
|
|
|
|
|
|
def calc_value(image):
|
|
|
|
grayscale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
|
|
return np.std(grayscale)
|
|
|
|
|
|
|
|
corner1 = (-1, -1)
|
|
|
|
corner2 = (-1, -1)
|
|
|
|
drawing = False
|
|
|
|
cropped_image = None
|
|
|
|
|
|
|
|
for videofilename in video_filenames:
|
|
|
|
|
|
|
|
main_window.title(f"Video Processing App")
|
|
|
|
|
|
|
|
video_file_src = videofilename
|
|
|
|
|
|
|
|
# Define videofilename_base based on the video file name
|
|
|
|
videofilename_base, _ = os.path.splitext(os.path.basename(videofilename))
|
|
|
|
|
|
|
|
cap = cv2.VideoCapture(video_file_src, 0)
|
|
|
|
|
|
|
|
if not cap.isOpened():
|
|
|
|
write_log(f"Error: Could not open video file: {video_file_src}")
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
counter = 0
|
|
|
|
frame_cnt = 0
|
|
|
|
discarded_frames = 0
|
|
|
|
|
|
|
|
# Create the output folder early on
|
|
|
|
output_folder = create_output_directory(videofilename)
|
|
|
|
|
|
|
|
# Create the "Discarded Frames" subfolder within the output folder
|
|
|
|
discarded_folder = os.path.join(output_folder, 'Discarded Frames')
|
|
|
|
if not os.path.exists(discarded_folder) and SAVE:
|
|
|
|
os.makedirs(discarded_folder)
|
|
|
|
|
|
|
|
write_log(f"[DEBUG] Output Folder: {output_folder}\n[DEBUG] Discarded Folder: {discarded_folder}")
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize ROI parameters from preferences
|
|
|
|
roi_size = int(roi_size_var.get())
|
|
|
|
vertical_alignment = vertical_alignment_var.get()
|
|
|
|
horizontal_alignment = horizontal_alignment_var.get()
|
|
|
|
|
|
|
|
if SHOW:
|
|
|
|
vp_title = "Video2Crops Video Player"
|
|
|
|
# Get the screen resolution
|
|
|
|
screen = screeninfo.get_monitors()[0]
|
|
|
|
screen_width, screen_height = screen.width, screen.height
|
|
|
|
|
|
|
|
# Calculate the initial window size as a quarter of the screen resolution
|
|
|
|
initial_width = screen_width // 2
|
|
|
|
initial_height = screen_height // 2
|
|
|
|
|
|
|
|
# Create the video player window
|
|
|
|
cv2.namedWindow(vp_title, cv2.WINDOW_NORMAL)
|
|
|
|
cv2.resizeWindow(vp_title, initial_width, initial_height)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
ret, frame = cap.read()
|
|
|
|
|
|
|
|
if not ret:
|
|
|
|
break
|
|
|
|
|
|
|
|
# Get the video width and height from the frame
|
|
|
|
video_width = frame.shape[1]
|
|
|
|
video_height = frame.shape[0]
|
|
|
|
|
|
|
|
x1, y1, x2, y2 = calculate_roi(video_width, video_height, vertical_alignment, horizontal_alignment, roi_size)
|
|
|
|
|
|
|
|
cropped_image_status = "Rejected"
|
|
|
|
|
|
|
|
frame_copy = frame.copy()
|
|
|
|
|
|
|
|
if drawing:
|
|
|
|
cv2.rectangle(frame_copy, corner1, corner2, (0, 255, 0), 3)
|
|
|
|
|
|
|
|
test = cv2.rectangle(frame.copy(), (x1 - 1, y2 - 1), (x2 + 1, y1 + 1), (255, 0, 0), 4)
|
|
|
|
cropped_image = frame[y1:y2, x1:x2]
|
|
|
|
|
|
|
|
if SHOW:
|
|
|
|
cv2.imshow(vp_title, test)
|
|
|
|
|
|
|
|
key = cv2.waitKey(10)
|
|
|
|
|
|
|
|
if key == ord('q'):
|
|
|
|
break
|
|
|
|
|
|
|
|
if cropped_image is not None and frame_cnt % frame_step == 0:
|
|
|
|
if SHOW:
|
|
|
|
ci_title = "Video2Crops Cropped Image"
|
|
|
|
cv2.namedWindow(ci_title, cv2.WINDOW_NORMAL)
|
|
|
|
cv2.imshow(ci_title, cropped_image)
|
|
|
|
|
|
|
|
frame_value = calc_value(cropped_image)
|
|
|
|
if frame_value >= value_threshold:
|
|
|
|
cropped_image_status = "Accepted"
|
|
|
|
|
|
|
|
filename = videofilename_base + '_' + str(counter).zfill(4) + ".png"
|
|
|
|
image_filename = os.path.join(output_folder, filename)
|
|
|
|
|
|
|
|
if SAVE:
|
|
|
|
cv2.imwrite(image_filename, cropped_image)
|
|
|
|
|
|
|
|
cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status}) - {frame_value} / {value_threshold} - image nr: {str(str(counter)).zfill(4)}"
|
|
|
|
write_log(cropped_image_info)
|
|
|
|
counter += 1
|
|
|
|
else:
|
|
|
|
filename = videofilename_base + '_' + str(discarded_frames).zfill(4) + ".png"
|
|
|
|
image_filename = os.path.join(discarded_folder, filename)
|
|
|
|
if SAVE:
|
|
|
|
cv2.imwrite(image_filename, cropped_image)
|
|
|
|
|
|
|
|
cropped_image_info = f"Image on frame {frame_cnt} ({cropped_image_status}) - {frame_value} / {value_threshold} - image nr: {str(str(counter)).zfill(4)}"
|
|
|
|
write_log(cropped_image_info)
|
|
|
|
discarded_frames += 1
|
|
|
|
|
|
|
|
frame_cnt += 1
|
|
|
|
|
|
|
|
cap.release()
|
|
|
|
cv2.destroyAllWindows()
|
|
|
|
|
|
|
|
write_log(f"Video2Crops run complete!\nSaved {counter} frames.\nDiscarded {discarded_frames} frames.")
|
|
|
|
|
|
|
|
processing = False
|
|
|
|
|
|
|
|
|
|
|
|
# Create a function to add files to the selected files Listbox
|
|
|
|
def add_files():
|
|
|
|
file_paths = filedialog.askopenfilenames(filetypes=[("Video Files", "*.mp4 *.avi *.mov")])
|
|
|
|
if file_paths:
|
|
|
|
for file_path in file_paths:
|
|
|
|
selected_files_listbox.insert(tk.END, file_path)
|
|
|
|
|
|
|
|
# Create a button to remove the selected file from the Listbox
|
|
|
|
def remove_file():
|
|
|
|
selected_index = selected_files_listbox.curselection()
|
|
|
|
if selected_index:
|
|
|
|
selected_files_listbox.delete(selected_index)
|
|
|
|
|
|
|
|
# Create a function to clear the queue
|
|
|
|
def clear_queue():
|
|
|
|
selected_files_listbox.delete(0, tk.END)
|
|
|
|
|
|
|
|
# Create a function to cancel processing
|
|
|
|
def cancel_processing():
|
|
|
|
global processing
|
|
|
|
if processing:
|
|
|
|
processing = False
|
|
|
|
messagebox.showinfo("Processing Canceled", "Video processing has been canceled.")
|
|
|
|
|
|
|
|
# Create a button to process the selected files
|
|
|
|
def process_files():
|
|
|
|
global processing
|
|
|
|
if not processing:
|
|
|
|
selected_files = selected_files_listbox.get(0, tk.END)
|
|
|
|
for file_path in selected_files:
|
|
|
|
process_video([file_path], root)
|
|
|
|
|
|
|
|
# Create a function to open the preferences window
|
|
|
|
def open_preferences():
|
|
|
|
|
|
|
|
preferences_window = tk.Toplevel(root)
|
|
|
|
preferences_window.title("Preferences")
|
|
|
|
|
|
|
|
def change_frame_step():
|
|
|
|
if not processing:
|
|
|
|
new_frame_step = simpledialog.askinteger("Frame Step", "Enter the new frame step:")
|
|
|
|
if new_frame_step is not None:
|
|
|
|
frame_step_var.set(str(new_frame_step))
|
|
|
|
global frame_step
|
|
|
|
frame_step = new_frame_step
|
|
|
|
|
|
|
|
def change_value_threshold():
|
|
|
|
if not processing:
|
|
|
|
new_value_threshold = simpledialog.askfloat("Value Threshold", "Enter the new value threshold:")
|
|
|
|
if new_value_threshold is not None:
|
|
|
|
value_threshold_var.set(str(new_value_threshold))
|
|
|
|
|
|
|
|
def toggle_save():
|
|
|
|
if not processing:
|
|
|
|
global SAVE
|
|
|
|
SAVE = not SAVE
|
|
|
|
|
|
|
|
def toggle_show():
|
|
|
|
if not processing:
|
|
|
|
global SHOW
|
|
|
|
SHOW = not SHOW
|
|
|
|
|
|
|
|
def apply_roi_size():
|
|
|
|
if not processing:
|
|
|
|
new_roi_size = simpledialog.askinteger("ROI Size", "Enter the new ROI size:")
|
|
|
|
if new_roi_size is not None:
|
|
|
|
roi_size_var.set(str(new_roi_size))
|
|
|
|
|
|
|
|
# Create preference fields
|
|
|
|
frame_step_label = tk.Label(preferences_window, text="Skip n frames:")
|
|
|
|
frame_step_label.grid(row=0, column=0, padx=20, pady=10)
|
|
|
|
frame_step_value = tk.Label(preferences_window, textvariable=frame_step_var)
|
|
|
|
frame_step_value.grid(row=0, column=1, padx=20, pady=10)
|
|
|
|
frame_step_button = tk.Button(preferences_window, text="Edit", command=change_frame_step)
|
|
|
|
frame_step_button.grid(row=0, column=2, padx=20, pady=10)
|
|
|
|
|
|
|
|
value_threshold_label = tk.Label(preferences_window, text="Discard Threshold:")
|
|
|
|
value_threshold_label.grid(row=1, column=0, padx=20, pady=10)
|
|
|
|
value_threshold_value = tk.Label(preferences_window, textvariable=value_threshold_var)
|
|
|
|
value_threshold_value.grid(row=1, column=1, padx=20, pady=10)
|
|
|
|
value_threshold_button = tk.Button(preferences_window, text="Edit", command=change_value_threshold)
|
|
|
|
value_threshold_button.grid(row=1, column=2, padx=20, pady=10)
|
|
|
|
|
|
|
|
save_checkbox = tk.Checkbutton(preferences_window, text="Save Output Images", variable=save_var, command=toggle_save)
|
|
|
|
save_checkbox.grid(row=2, column=0, columnspan=3, padx=20, pady=10)
|
|
|
|
|
|
|
|
show_checkbox = tk.Checkbutton(preferences_window, text="Display Playback", variable=show_var, command=toggle_show)
|
|
|
|
show_checkbox.grid(row=3, column=0, columnspan=3, padx=20, pady=10)
|
|
|
|
|
|
|
|
# Section divider
|
|
|
|
divider_label = tk.Label(preferences_window, text="ROI Settings", font=("Helvetica", 12, "bold"))
|
|
|
|
divider_label.grid(row=4, column=0, columnspan=3, padx=20, pady=(20, 10))
|
|
|
|
|
|
|
|
# Create ROI fields
|
|
|
|
vertical_alignment_label = tk.Label(preferences_window, text="Vertical Alignment:")
|
|
|
|
vertical_alignment_label.grid(row=5, column=0, padx=20, pady=10)
|
|
|
|
vertical_alignment_var.set("Center") # Default alignment
|
|
|
|
vertical_alignment_menu = tk.OptionMenu(preferences_window, vertical_alignment_var, "Top", "Center", "Bottom")
|
|
|
|
vertical_alignment_menu.grid(row=5, column=1, padx=20, pady=10)
|
|
|
|
|
|
|
|
horizontal_alignment_label = tk.Label(preferences_window, text="Horizontal Alignment:")
|
|
|
|
horizontal_alignment_label.grid(row=6, column=0, padx=20, pady=10)
|
|
|
|
horizontal_alignment_var.set("Center") # Default alignment
|
|
|
|
horizontal_alignment_menu = tk.OptionMenu(preferences_window, horizontal_alignment_var, "Left", "Center", "Right")
|
|
|
|
horizontal_alignment_menu.grid(row=6, column=1, padx=20, pady=10)
|
|
|
|
|
|
|
|
roi_size_label = tk.Label(preferences_window, text="ROI Size:")
|
|
|
|
roi_size_label.grid(row=7, column=0, padx=20, pady=10)
|
|
|
|
roi_size_value = tk.Label(preferences_window, textvariable=roi_size_var)
|
|
|
|
roi_size_value.grid(row=7, column=1, padx=20, pady=10)
|
|
|
|
roi_size_button = tk.Button(preferences_window, text="Edit", command=apply_roi_size)
|
|
|
|
roi_size_button.grid(row=7, column=2, padx=20, pady=10)
|
|
|
|
|
|
|
|
# Create a menu
|
|
|
|
menu = tk.Menu(root)
|
|
|
|
root.config(menu=menu)
|
|
|
|
|
|
|
|
# Variables for preferences
|
|
|
|
frame_step_var = tk.StringVar()
|
|
|
|
value_threshold_var = tk.StringVar()
|
|
|
|
save_var = tk.BooleanVar()
|
|
|
|
show_var = tk.BooleanVar()
|
|
|
|
|
|
|
|
# Variables for ROI
|
|
|
|
vertical_alignment_var = tk.StringVar()
|
|
|
|
horizontal_alignment_var = tk.StringVar()
|
|
|
|
roi_size_var = tk.StringVar()
|
|
|
|
roi_size_var.set("400")
|
|
|
|
|
|
|
|
# Set initial values for preference variables
|
|
|
|
frame_step_var.set(str(frame_step))
|
|
|
|
value_threshold_var.set(str(value_threshold))
|
|
|
|
save_var.set(SAVE)
|
|
|
|
show_var.set(SHOW)
|
|
|
|
|
|
|
|
# Create a "Queue" submenu
|
|
|
|
queue_menu = tk.Menu(menu, tearoff=0)
|
|
|
|
menu.add_cascade(label="Queue", menu=queue_menu)
|
|
|
|
queue_menu.add_command(label="Add file", command=add_files)
|
|
|
|
queue_menu.add_command(label="Clear queue", command=clear_queue)
|
|
|
|
queue_menu.add_separator()
|
|
|
|
queue_menu.add_command(label="Cancel", command=cancel_processing)
|
|
|
|
queue_menu.add_command(label="Cancel & Clear", command=lambda: [cancel_processing(), clear_queue()])
|
|
|
|
|
|
|
|
# Create a "Preferences" submenu
|
|
|
|
menu.add_command(label="Preferences", command=open_preferences)
|
|
|
|
|
|
|
|
# Create an "Info" submenu
|
|
|
|
info_menu = tk.Menu(menu, tearoff=0)
|
|
|
|
menu.add_cascade(label="Info", menu=info_menu)
|
|
|
|
info_menu.add_command(label="Project Folder", command=lambda: webbrowser.open(os.getcwd()))
|
|
|
|
info_menu.add_separator()
|
2023-10-01 13:10:23 +00:00
|
|
|
info_menu.add_command(label="About", command=lambda: webbrowser.open("https://git.rolfsvaag.no/frarol96/Video2Crops/wiki"))
|
2023-09-30 10:12:06 +00:00
|
|
|
info_menu.add_command(label=f"Version: {str(v2cversion)}")
|
2023-10-01 13:10:23 +00:00
|
|
|
info_menu.add_command(label=f"Check for updates", command=check_for_updates)
|
|
|
|
info_menu.add_command(label=f"View Changelog", command=open_changelog_window)
|
2023-09-30 10:12:06 +00:00
|
|
|
|
|
|
|
# Create a label for selected files
|
|
|
|
label = tk.Label(root, text="Selected Files:")
|
|
|
|
label.pack(padx=20, pady=10)
|
|
|
|
|
|
|
|
# Create a Listbox to display the selected files
|
|
|
|
selected_files_listbox = tk.Listbox(root, selectmode=tk.SINGLE, exportselection=0)
|
|
|
|
selected_files_listbox.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
|
|
|
|
|
|
|
|
# Create a button to select and add files to the Listbox
|
|
|
|
select_files_button = tk.Button(root, text="Select Files", command=add_files)
|
|
|
|
select_files_button.pack(padx=20, pady=10)
|
|
|
|
|
|
|
|
# Create a button to remove the selected file from the Listbox
|
|
|
|
remove_file_button = tk.Button(root, text="Remove File", command=remove_file)
|
|
|
|
remove_file_button.pack(padx=20, pady=10)
|
|
|
|
|
|
|
|
# Create a button to process the selected files
|
|
|
|
process_files_button = tk.Button(root, text="Process Files", command=process_files)
|
|
|
|
process_files_button.pack(padx=20, pady=10)
|
|
|
|
|
2023-10-01 13:10:23 +00:00
|
|
|
root.mainloop()
|