diff --git a/src/upscaler.py b/src/upscaler.py
index af5157b..5e33b78 100755
--- a/src/upscaler.py
+++ b/src/upscaler.py
@@ -4,7 +4,7 @@
Name: Video2X Upscaler
Author: K4YT3X
Date Created: December 10, 2018
-Last Modified: May 7, 2020
+Last Modified: May 8, 2020
Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or
@@ -30,9 +30,7 @@ import queue
import re
import shutil
import subprocess
-import sys
import tempfile
-import threading
import time
import traceback
@@ -71,8 +69,8 @@ class Upscaler:
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments
- self.input_path = input_path
- self.output_path = output_path
+ self.input = input_path
+ self.output = output_path
self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings
@@ -134,20 +132,30 @@ class Upscaler:
traceback.print_exc()
def _check_arguments(self):
+ if isinstance(self.input, list):
+ if self.output.exists() and not self.output.is_dir():
+ Avalon.error(_('Input and output path type mismatch'))
+ Avalon.error(_('Input is multiple files but output is not directory'))
+ raise ArgumentError('input output path type mismatch')
+ for input_path in self.input:
+ if not input_path.is_file() and not input_path.is_dir():
+ Avalon.error(_('Input path {} is neither a file nor a directory'.format(input_path)))
+ raise FileNotFoundError(f'{input_path} is neither file nor directory')
+
# if input is a file
- if self.input_path.is_file():
- if self.output_path.is_dir():
+ elif self.input.is_file():
+ if self.output.is_dir():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory'))
raise ArgumentError('input output path type mismatch')
- if not re.search(r'.*\..*$', str(self.output_path)):
+ if not re.search(r'.*\..*$', str(self.output)):
Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified')
# if input is a directory
- elif self.input_path.is_dir():
- if self.output_path.is_file():
+ elif self.input.is_dir():
+ if self.output.is_file():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is directory but output is existing single file'))
raise ArgumentError('input output path type mismatch')
@@ -155,7 +163,7 @@ class Upscaler:
# if input is neither
else:
Avalon.error(_('Input path is neither a file nor a directory'))
- raise FileNotFoundError(f'{self.input_path} is neither file nor directory')
+ raise FileNotFoundError(f'{self.input} is neither file nor directory')
# check Fmpeg settings
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
@@ -259,32 +267,32 @@ class Upscaler:
# if the driver being used is waifu2x-caffe
if self.driver == 'waifu2x_caffe':
self.process_pool.append(driver.upscale(process_directory,
- self.upscaled_frames,
- self.scale_ratio,
- self.scale_width,
- self.scale_height,
- self.image_format,
- self.bit_depth))
+ self.upscaled_frames,
+ self.scale_ratio,
+ self.scale_width,
+ self.scale_height,
+ self.image_format,
+ self.bit_depth))
# if the driver being used is waifu2x-converter-cpp
elif self.driver == 'waifu2x_converter_cpp':
self.process_pool.append(driver.upscale(process_directory,
- self.upscaled_frames,
- self.scale_ratio,
- self.processes,
- self.image_format))
+ self.upscaled_frames,
+ self.scale_ratio,
+ self.processes,
+ self.image_format))
# if the driver being used is waifu2x-ncnn-vulkan
elif self.driver == 'waifu2x_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory,
- self.upscaled_frames,
- self.scale_ratio))
+ self.upscaled_frames,
+ self.scale_ratio))
# if the driver being used is srmd_ncnn_vulkan
elif self.driver == 'srmd_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory,
- self.upscaled_frames,
- self.scale_ratio))
+ self.upscaled_frames,
+ self.scale_ratio))
# start progress bar in a different thread
Avalon.debug_info(_('Starting progress monitor'))
@@ -390,18 +398,34 @@ class Upscaler:
# define processing queue
processing_queue = queue.Queue()
+ # if input is a list of files
+ if isinstance(self.input, list):
+ # make output directory if it doesn't exist
+ self.output.mkdir(parents=True, exist_ok=True)
+
+ for input_path in self.input:
+
+ if input_path.is_file():
+ output_video = self.output / input_path.name
+ processing_queue.put((input_path.absolute(), output_video.absolute()))
+
+ elif input_path.is_dir():
+ for input_video in [f for f in input_path.iterdir() if f.is_file()]:
+ output_video = self.output / input_video.name
+ processing_queue.put((input_video.absolute(), output_video.absolute()))
+
# if input specified is single file
- if self.input_path.is_file():
- Avalon.info(_('Upscaling single video file: {}').format(self.input_path))
- processing_queue.put((self.input_path.absolute(), self.output_path.absolute()))
+ elif self.input.is_file():
+ Avalon.info(_('Upscaling single video file: {}').format(self.input))
+ processing_queue.put((self.input.absolute(), self.output.absolute()))
# if input specified is a directory
- elif self.input_path.is_dir():
+ elif self.input.is_dir():
# make output directory if it doesn't exist
- self.output_path.mkdir(parents=True, exist_ok=True)
- for input_video in [f for f in self.input_path.iterdir() if f.is_file()]:
- output_video = self.output_path / input_video.name
+ self.output.mkdir(parents=True, exist_ok=True)
+ for input_video in [f for f in self.input.iterdir() if f.is_file()]:
+ output_video = self.output / input_video.name
processing_queue.put((input_video.absolute(), output_video.absolute()))
while not processing_queue.empty():
diff --git a/src/video2x_gui.py b/src/video2x_gui.py
index b5a3050..7592a53 100755
--- a/src/video2x_gui.py
+++ b/src/video2x_gui.py
@@ -4,7 +4,7 @@
Creator: Video2X GUI
Author: K4YT3X
Date Created: May 5, 2020
-Last Modified: May 7, 2020
+Last Modified: May 8, 2020
"""
# local imports
@@ -14,7 +14,6 @@ from upscaler import Upscaler
import contextlib
import os
import pathlib
-import re
import sys
import tempfile
import time
@@ -22,9 +21,10 @@ import traceback
import yaml
# third-party imports
-from PyQt5 import QtWidgets, QtGui
-from PyQt5 import uic
-from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool
+from PyQt5 import QtGui, uic
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
VERSION = '2.0.0'
@@ -42,6 +42,7 @@ AVAILABLE_DRIVERS = {
'Anime4KCPP': 'anime4kcpp'
}
+
def resource_path(relative_path: str) -> pathlib.Path:
try:
base_path = pathlib.Path(sys._MEIPASS)
@@ -56,6 +57,7 @@ class WorkerSignals(QObject):
interrupted = pyqtSignal()
finished = pyqtSignal()
+
class ProgressBarWorker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(ProgressBarWorker, self).__init__()
@@ -72,6 +74,7 @@ class ProgressBarWorker(QRunnable):
except Exception:
pass
+
class UpscalerWorker(QRunnable):
def __init__(self, fn, *args, **kwargs):
@@ -99,7 +102,50 @@ class UpscalerWorker(QRunnable):
self.signals.finished.emit()
-class Video2XMainWindow(QtWidgets.QMainWindow):
+class InputTableModel(QAbstractTableModel):
+ def __init__(self, data):
+ super(InputTableModel, self).__init__()
+ self._data = data
+
+ def data(self, index, role):
+ if role == Qt.DisplayRole:
+
+ if index.column() == 0:
+ return str(self._data[index.row()].absolute())
+ else:
+ if self._data[index.row()].is_file():
+ return 'File'
+ elif self._data[index.row()].is_dir():
+ return 'Folder'
+ else:
+ return 'Unknown'
+
+ def rowCount(self, index):
+ return len(self._data)
+
+ def columnCount(self, index):
+ return 2
+
+ def removeRow(self, index):
+ self._data.pop(index)
+
+ def headerData(self, section, orientation, role):
+ # section is the index of the column/row.
+ if role != Qt.DisplayRole:
+ return None
+
+ horizontal_headers = ['File Path', 'Type']
+
+ # return the correspondign header
+ if orientation == Qt.Horizontal:
+ return horizontal_headers[section]
+
+ # simply return the line number
+ if orientation == Qt.Vertical:
+ return str(section)
+
+
+class Video2XMainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -114,131 +160,147 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
# menu bar
- self.action_exit = self.findChild(QtWidgets.QAction, 'actionExit')
+ self.action_exit = self.findChild(QAction, 'actionExit')
self.action_exit.triggered.connect(sys.exit)
- self.action_about = self.findChild(QtWidgets.QAction, 'actionAbout')
+ self.action_about = self.findChild(QAction, 'actionAbout')
self.action_about.triggered.connect(lambda: self.show_message(LEGAL_INFO, custom_icon=QtGui.QPixmap(self.video2x_icon_path)))
# main tab
# select input file/folder
- self.input_line_edit = self.findChild(QtWidgets.QLineEdit, 'inputLineEdit')
- self.enable_line_edit_file_drop(self.input_line_edit)
- self.input_select_file_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFileButton')
- self.input_select_file_button.clicked.connect(self.select_input_file)
- self.input_select_folder_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFolderButton')
- self.input_select_folder_button.clicked.connect(self.select_input_folder)
+ self.input_table_view = self.findChild(QTableView, 'inputTableView')
+ self.input_table_view.dragEnterEvent = self.dragEnterEvent
+ self.input_table_view.dropEvent = self.dropEvent
+ # initialize data in table
+ self.input_table_data = []
+ self.input_table_model = InputTableModel(self.input_table_data)
+ self.input_table_view.setModel(self.input_table_model)
+ # stretch file path and fill columns horizontally
+ self.input_table_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
+
+ # input table buttons
+ self.input_select_file_button = self.findChild(QPushButton, 'inputSelectFileButton')
+ self.input_select_file_button.clicked.connect(self.select_input_file)
+ self.input_select_folder_button = self.findChild(QPushButton, 'inputSelectFolderButton')
+ self.input_select_folder_button.clicked.connect(self.select_input_folder)
+ self.input_delete_selected_button = self.findChild(QPushButton, 'inputDeleteSelectedButton')
+ self.input_delete_selected_button.clicked.connect(self.input_table_delete_selected)
+ self.input_clear_all_button = self.findChild(QPushButton, 'inputClearAllButton')
+ self.input_clear_all_button.clicked.connect(self.input_table_clear_all)
+
+ # other paths selection
# select output file/folder
- self.output_line_edit = self.findChild(QtWidgets.QLineEdit, 'outputLineEdit')
+ self.output_line_edit = self.findChild(QLineEdit, 'outputLineEdit')
self.enable_line_edit_file_drop(self.output_line_edit)
- self.output_select_file_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFileButton')
+ self.output_line_edit.setText(str((pathlib.Path().cwd() / 'output').absolute()))
+ self.output_select_file_button = self.findChild(QPushButton, 'outputSelectFileButton')
self.output_select_file_button.clicked.connect(self.select_output_file)
- self.output_select_folder_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFolderButton')
+ self.output_select_folder_button = self.findChild(QPushButton, 'outputSelectFolderButton')
self.output_select_folder_button.clicked.connect(self.select_output_folder)
# config file
- self.config_line_edit = self.findChild(QtWidgets.QLineEdit, 'configLineEdit')
+ self.config_line_edit = self.findChild(QLineEdit, 'configLineEdit')
self.enable_line_edit_file_drop(self.config_line_edit)
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute()))
- self.config_select_file_button = self.findChild(QtWidgets.QPushButton, 'configSelectButton')
+ self.config_select_file_button = self.findChild(QPushButton, 'configSelectButton')
self.config_select_file_button.clicked.connect(self.select_config_file)
# cache directory
- self.cache_line_edit = self.findChild(QtWidgets.QLineEdit, 'cacheLineEdit')
+ self.cache_line_edit = self.findChild(QLineEdit, 'cacheLineEdit')
self.enable_line_edit_file_drop(self.cache_line_edit)
- self.cache_select_folder_button = self.findChild(QtWidgets.QPushButton, 'cacheSelectFolderButton')
+ self.cache_select_folder_button = self.findChild(QPushButton, 'cacheSelectFolderButton')
self.cache_select_folder_button.clicked.connect(self.select_cache_folder)
# express settings
- self.driver_combo_box = self.findChild(QtWidgets.QComboBox, 'driverComboBox')
+ self.driver_combo_box = self.findChild(QComboBox, 'driverComboBox')
self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
- self.processes_spin_box = self.findChild(QtWidgets.QSpinBox, 'processesSpinBox')
- self.scale_ratio_double_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
- self.preserve_frames_check_box = self.findChild(QtWidgets.QCheckBox, 'preserveFramesCheckBox')
+ self.processes_spin_box = self.findChild(QSpinBox, 'processesSpinBox')
+ self.scale_ratio_double_spin_box = self.findChild(QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
+ self.preserve_frames_check_box = self.findChild(QCheckBox, 'preserveFramesCheckBox')
# progress bar and start/stop controls
- self.progress_bar = self.findChild(QtWidgets.QProgressBar, 'progressBar')
- self.time_elapsed_label = self.findChild(QtWidgets.QLabel, 'timeElapsedLabel')
- self.time_remaining_label = self.findChild(QtWidgets.QLabel, 'timeRemainingLabel')
- self.rate_label = self.findChild(QtWidgets.QLabel, 'rateLabel')
- self.start_button = self.findChild(QtWidgets.QPushButton, 'startButton')
+ self.progress_bar = self.findChild(QProgressBar, 'progressBar')
+ self.time_elapsed_label = self.findChild(QLabel, 'timeElapsedLabel')
+ self.time_remaining_label = self.findChild(QLabel, 'timeRemainingLabel')
+ self.rate_label = self.findChild(QLabel, 'rateLabel')
+ self.start_button = self.findChild(QPushButton, 'startButton')
self.start_button.clicked.connect(self.start)
- self.stop_button = self.findChild(QtWidgets.QPushButton, 'stopButton')
+ self.stop_button = self.findChild(QPushButton, 'stopButton')
self.stop_button.clicked.connect(self.stop)
# driver settings
# waifu2x-caffe
- self.waifu2x_caffe_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xCaffePathLineEdit')
+ self.waifu2x_caffe_path_line_edit = self.findChild(QLineEdit, 'waifu2xCaffePathLineEdit')
self.enable_line_edit_file_drop(self.waifu2x_caffe_path_line_edit)
- self.waifu2x_caffe_path_select_button = self.findChild(QtWidgets.QPushButton, 'waifu2xCaffePathSelectButton')
+ self.waifu2x_caffe_path_select_button = self.findChild(QPushButton, 'waifu2xCaffePathSelectButton')
self.waifu2x_caffe_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_caffe_path_line_edit))
- self.waifu2x_caffe_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModeComboBox')
- self.waifu2x_caffe_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
- self.waifu2x_caffe_process_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeProcessComboBox')
- self.waifu2x_caffe_model_combobox = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModelComboBox')
- self.waifu2x_caffe_crop_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeCropSizeSpinBox')
- self.waifu2x_caffe_output_quality_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputQualitySpinBox')
- self.waifu2x_caffe_output_depth_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputDepthSpinBox')
- self.waifu2x_caffe_batch_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeBatchSizeSpinBox')
- self.waifu2x_caffe_gpu_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeGpuSpinBox')
- self.waifu2x_caffe_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xCaffeTtaCheckBox')
+ self.waifu2x_caffe_mode_combo_box = self.findChild(QComboBox, 'waifu2xCaffeModeComboBox')
+ self.waifu2x_caffe_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
+ self.waifu2x_caffe_process_combo_box = self.findChild(QComboBox, 'waifu2xCaffeProcessComboBox')
+ self.waifu2x_caffe_model_combobox = self.findChild(QComboBox, 'waifu2xCaffeModelComboBox')
+ self.waifu2x_caffe_crop_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeCropSizeSpinBox')
+ self.waifu2x_caffe_output_quality_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputQualitySpinBox')
+ self.waifu2x_caffe_output_depth_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputDepthSpinBox')
+ self.waifu2x_caffe_batch_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeBatchSizeSpinBox')
+ self.waifu2x_caffe_gpu_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeGpuSpinBox')
+ self.waifu2x_caffe_tta_check_box = self.findChild(QCheckBox, 'waifu2xCaffeTtaCheckBox')
# waifu2x-converter-cpp
- self.waifu2x_converter_cpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xConverterCppPathLineEdit')
+ self.waifu2x_converter_cpp_path_line_edit = self.findChild(QLineEdit, 'waifu2xConverterCppPathLineEdit')
self.enable_line_edit_file_drop(self.waifu2x_converter_cpp_path_line_edit)
- self.waifu2x_converter_cpp_path_edit_button = self.findChild(QtWidgets.QPushButton, 'waifu2xConverterCppPathSelectButton')
+ self.waifu2x_converter_cpp_path_edit_button = self.findChild(QPushButton, 'waifu2xConverterCppPathSelectButton')
self.waifu2x_converter_cpp_path_edit_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_converter_cpp_path_line_edit))
- self.waifu2x_converter_cpp_png_compression_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
- self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
- self.waifu2x_converter_cpp_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModelComboBox')
- self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModeComboBox')
- self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QtWidgets.QCheckBox, 'disableGpuCheckBox')
- self.waifu2x_converter_cpp_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'ttaCheckBox')
+ self.waifu2x_converter_cpp_png_compression_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
+ self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
+ self.waifu2x_converter_cpp_model_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModelComboBox')
+ self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModeComboBox')
+ self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QCheckBox, 'disableGpuCheckBox')
+ self.waifu2x_converter_cpp_tta_check_box = self.findChild(QCheckBox, 'ttaCheckBox')
# waifu2x-ncnn-vulkan
- self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanPathLineEdit')
+ self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanPathLineEdit')
self.enable_line_edit_file_drop(self.waifu2x_ncnn_vulkan_path_line_edit)
- self.waifu2x_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'waifu2xNcnnVulkanPathSelectButton')
+ self.waifu2x_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'waifu2xNcnnVulkanPathSelectButton')
self.waifu2x_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_ncnn_vulkan_path_line_edit))
- self.waifu2x_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox')
- self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox')
- self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xNcnnVulkanModelComboBox')
- self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox')
- self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit')
- self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox')
+ self.waifu2x_ncnn_vulkan_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox')
+ self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox')
+ self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'waifu2xNcnnVulkanModelComboBox')
+ self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox')
+ self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit')
+ self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox')
# srmd-ncnn-vulkan
- self.srmd_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanPathLineEdit')
+ self.srmd_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanPathLineEdit')
self.enable_line_edit_file_drop(self.srmd_ncnn_vulkan_path_line_edit)
- self.srmd_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'srmdNcnnVulkanPathSelectButton')
+ self.srmd_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'srmdNcnnVulkanPathSelectButton')
self.srmd_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.srmd_ncnn_vulkan_path_line_edit))
- self.srmd_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox')
- self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox')
- self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'srmdNcnnVulkanModelComboBox')
- self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox')
- self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
- self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
+ self.srmd_ncnn_vulkan_noise_level_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox')
+ self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox')
+ self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'srmdNcnnVulkanModelComboBox')
+ self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox')
+ self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
+ self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# anime4k
- self.anime4kcpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'anime4kCppPathLineEdit')
+ self.anime4kcpp_path_line_edit = self.findChild(QLineEdit, 'anime4kCppPathLineEdit')
self.enable_line_edit_file_drop(self.anime4kcpp_path_line_edit)
- self.anime4kcpp_path_select_button = self.findChild(QtWidgets.QPushButton, 'anime4kCppPathSelectButton')
+ self.anime4kcpp_path_select_button = self.findChild(QPushButton, 'anime4kCppPathSelectButton')
self.anime4kcpp_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.anime4kcpp_path_line_edit))
- self.anime4kcpp_passes_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPassesSpinBox')
- self.anime4kcpp_push_color_count_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPushColorCountSpinBox')
- self.anime4kcpp_strength_color_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox')
- self.anime4kcpp_strength_gradient_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox')
- self.anime4kcpp_threads_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppThreadsSpinBox')
- self.anime4kcpp_pre_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPreFiltersSpinBox')
- self.anime4kcpp_post_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPostFiltersSpinBox')
- self.anime4kcpp_platform_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPlatformIdSpinBox')
- self.anime4kcpp_device_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppDeviceIdSpinBox')
- self.anime4kcpp_codec_combo_box = self.findChild(QtWidgets.QComboBox, 'anime4kCppCodecComboBox')
- self.anime4kcpp_fast_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppFastModeCheckBox')
- self.anime4kcpp_pre_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPreProcessingCheckBox')
- self.anime4kcpp_post_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPostProcessingCheckBox')
- self.anime4kcpp_gpu_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppGpuModeCheckBox')
+ self.anime4kcpp_passes_spin_box = self.findChild(QSpinBox, 'anime4kCppPassesSpinBox')
+ self.anime4kcpp_push_color_count_spin_box = self.findChild(QSpinBox, 'anime4kCppPushColorCountSpinBox')
+ self.anime4kcpp_strength_color_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox')
+ self.anime4kcpp_strength_gradient_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox')
+ self.anime4kcpp_threads_spin_box = self.findChild(QSpinBox, 'anime4kCppThreadsSpinBox')
+ self.anime4kcpp_pre_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPreFiltersSpinBox')
+ self.anime4kcpp_post_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPostFiltersSpinBox')
+ self.anime4kcpp_platform_id_spin_box = self.findChild(QSpinBox, 'anime4kCppPlatformIdSpinBox')
+ self.anime4kcpp_device_id_spin_box = self.findChild(QSpinBox, 'anime4kCppDeviceIdSpinBox')
+ self.anime4kcpp_codec_combo_box = self.findChild(QComboBox, 'anime4kCppCodecComboBox')
+ self.anime4kcpp_fast_mode_check_box = self.findChild(QCheckBox, 'anime4kCppFastModeCheckBox')
+ self.anime4kcpp_pre_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPreProcessingCheckBox')
+ self.anime4kcpp_post_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPostProcessingCheckBox')
+ self.anime4kcpp_gpu_mode_check_box = self.findChild(QCheckBox, 'anime4kCppGpuModeCheckBox')
# load configurations
self.load_configurations()
@@ -250,11 +312,15 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
event.ignore()
def dropEvent(self, event):
- input_path = pathlib.Path(event.mimeData().urls()[0].toLocalFile())
- self.input_line_edit.setText(str(input_path.absolute()))
- self.generate_output_path(input_path)
+ input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
+ for path in input_paths:
+ if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
+ self.input_table_data.append(path)
- def enable_line_edit_file_drop(self, line_edit: QtWidgets.QLineEdit):
+ self.update_input_table()
+ self.update_output_path()
+
+ def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
@@ -279,7 +345,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
# if file doesn't exist, return
if not config_file_path.is_file():
- QtWidgets.QErrorMessage(self).showMessage('Video2X configuration file not found, please specify manually.')
+ QErrorMessage(self).showMessage('Video2X configuration file not found, please specify manually.')
return
# read configuration dict from config file
@@ -438,19 +504,50 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
else:
self.processes_spin_box.setValue(1)
+ def update_input_table(self):
+ # HACK: use insertRow, removeRow and signals
+ del self.input_table_model
+ self.input_table_model = InputTableModel(self.input_table_data)
+ self.input_table_view.setModel(self.input_table_model)
+
+ def input_table_delete_selected(self):
+ items_to_delete = []
+ for index in [i.row() for i in self.input_table_view.selectedIndexes()]:
+ items_to_delete.append(self.input_table_data[index])
+
+ for item in items_to_delete:
+ self.input_table_data.remove(item)
+ self.update_input_table()
+
+ def input_table_clear_all(self):
+ self.input_table_data = []
+ self.update_input_table()
+
+ def input_table_path_exists(self, input_path):
+ for path in self.input_table_data:
+ # not using Path.samefile since file may not exist
+ if str(path.absolute()) == str(input_path.absolute()):
+ return True
+ return False
+
def select_file(self, *args, **kwargs) -> pathlib.Path:
- file_selected = QtWidgets.QFileDialog.getOpenFileName(self, *args, **kwargs)
+ file_selected = QFileDialog.getOpenFileName(self, *args, **kwargs)
if not isinstance(file_selected, tuple) or file_selected[0] == '':
return None
return pathlib.Path(file_selected[0])
def select_folder(self, *args, **kwargs) -> pathlib.Path:
- folder_selected = QtWidgets.QFileDialog.getExistingDirectory(self, *args, **kwargs)
+ folder_selected = QFileDialog.getExistingDirectory(self, *args, **kwargs)
if folder_selected == '':
return None
return pathlib.Path(folder_selected)
- def generate_output_path(self, input_path: pathlib.Path):
+ def update_output_path(self):
+ # if there is more than one input
+ if len(self.input_table_data) != 1:
+ return
+
+ input_path = self.input_table_data[0]
# give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
@@ -466,23 +563,27 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4')
elif input_path.is_dir():
- output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_file_id}')
+ output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
def select_input_file(self):
- if (input_file := self.select_file('Select Input File')) is None:
+ if ((input_file := self.select_file('Select Input File')) is None or
+ self.input_table_path_exists(input_file)):
return
- self.generate_output_path(input_file)
- self.input_line_edit.setText(str(input_file.absolute()))
+ self.input_table_data.append(input_file)
+ self.update_output_path()
+ self.update_input_table()
def select_input_folder(self):
- if (input_folder := self.select_folder('Select Input Folder')) is None:
+ if ((input_folder := self.select_folder('Select Input Folder')) is None or
+ self.input_table_path_exists(input_folder)):
return
- self.generate_output_path(input_folder)
- self.input_line_edit.setText(str(input_folder.absolute()))
+ self.input_table_data.append(input_folder)
+ self.update_output_path()
+ self.update_input_table()
def select_output_file(self):
if (output_file := self.select_file('Select Output File')) is None:
@@ -511,15 +612,15 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
driver_line_edit.setText(str(driver_binary_path.absolute()))
def show_error(self, message: str):
- QtWidgets.QErrorMessage(self).showMessage(message.replace('\n', '
'))
+ QErrorMessage(self).showMessage(message.replace('\n', '
'))
def show_message(self, message: str, custom_icon=None):
- message_box = QtWidgets.QMessageBox()
+ message_box = QMessageBox()
message_box.setWindowTitle('Message')
if custom_icon:
message_box.setIconPixmap(custom_icon.scaled(64, 64))
else:
- message_box.setIcon(QtWidgets.QMessageBox.Information)
+ message_box.setIcon(QMessageBox.Information)
message_box.setText(message)
message_box.exec_()
@@ -542,9 +643,9 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
progress_percentage = 0
progress_callback.emit((progress_percentage,
- self.upscaler.total_frames_upscaled,
- self.upscaler.total_frames,
- upscale_begin_time))
+ self.upscaler.total_frames_upscaled,
+ self.upscaler.total_frames,
+ upscale_begin_time))
time.sleep(1)
# upscale process will stop at 99%
@@ -580,14 +681,17 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.begin_time = time.time()
# resolve input and output directories from GUI
- if self.input_line_edit.text().strip() == '':
- self.show_error('Input path not specified')
+ if len(self.input_table_data) == 0:
+ self.show_error('Input path unspecified')
return
if self.output_line_edit.text().strip() == '':
- self.show_error('Output path not specified')
+ self.show_error('Output path unspecified')
return
- input_directory = pathlib.Path(os.path.expandvars(self.input_line_edit.text()))
+ if len(self.input_table_data) == 1:
+ input_directory = self.input_table_data[0]
+ else:
+ input_directory = self.input_table_data
output_directory = pathlib.Path(os.path.expandvars(self.output_line_edit.text()))
# load driver settings from GUI
@@ -658,7 +762,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
# this file shouldn't be imported
if __name__ == '__main__':
- app = QtWidgets.QApplication(sys.argv)
+ app = QApplication(sys.argv)
window = Video2XMainWindow()
window.show()
app.exec_()
diff --git a/src/video2x_gui.ui b/src/video2x_gui.ui
index d61be73..b99a934 100644
--- a/src/video2x_gui.ui
+++ b/src/video2x_gui.ui
@@ -6,8 +6,8 @@
0
0
- 691
- 503
+ 718
+ 668
@@ -16,7 +16,7 @@
Video2X GUI
-
+
5
@@ -79,154 +79,160 @@
-
-
-
-
-
-
-
-
-
-
- 63
- 0
-
-
-
- 1
-
-
- Input
-
-
- false
-
-
- 0
-
-
-
- -
-
-
- -
-
-
- true
-
-
- Select File
-
-
-
- -
-
-
- Select Folder
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 63
- 0
-
-
-
- Output
-
-
-
- -
-
-
- -
-
-
- Select File
-
-
-
- -
-
-
- Select Folder
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 63
- 0
-
-
-
- Config
-
-
-
- -
-
-
- -
-
-
- Select
-
-
-
-
-
- -
-
-
-
-
-
-
- 63
- 0
-
-
-
- Cache Folder
-
-
-
- -
-
-
- -
-
-
- Select Folder
-
-
-
-
-
-
+
+
+ Input Selection
+
+
+ -
+
+
+ -
+
+
-
+
+
+ true
+
+
+ Select File
+
+
+
+ -
+
+
+ Select Folder
+
+
+
+ -
+
+
+ Delete Selected
+
+
+
+ -
+
+
+ Clear All
+
+
+
+
+
+
+
+
+ -
+
+
+ Other Paths Selection
+
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 63
+ 0
+
+
+
+ Output
+
+
+
+ -
+
+
+ -
+
+
+ Select File
+
+
+
+ -
+
+
+ Select Folder
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 63
+ 0
+
+
+
+ Config
+
+
+
+ -
+
+
+ -
+
+
+ Select
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 63
+ 0
+
+
+
+ Cache Folder
+
+
+
+ -
+
+
+ -
+
+
+ Select Folder
+
+
+
+
+
+
+
-
@@ -1344,7 +1350,7 @@
0
0
- 691
+ 718
21