upgraded input QLineEdit to QTableView

This commit is contained in:
k4yt3x 2020-05-08 17:37:16 -04:00
parent d12f2a3888
commit f2943802cb
3 changed files with 427 additions and 293 deletions

View File

@ -4,7 +4,7 @@
Name: Video2X Upscaler Name: Video2X Upscaler
Author: K4YT3X Author: K4YT3X
Date Created: December 10, 2018 Date Created: December 10, 2018
Last Modified: May 7, 2020 Last Modified: May 8, 2020
Description: This file contains the Upscaler class. Each Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or instance of the Upscaler class is an upscaler on an image or
@ -30,9 +30,7 @@ import queue
import re import re
import shutil import shutil
import subprocess import subprocess
import sys
import tempfile import tempfile
import threading
import time import time
import traceback import traceback
@ -71,8 +69,8 @@ class Upscaler:
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings): def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments # mandatory arguments
self.input_path = input_path self.input = input_path
self.output_path = output_path self.output = output_path
self.driver_settings = driver_settings self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings self.ffmpeg_settings = ffmpeg_settings
@ -134,20 +132,30 @@ class Upscaler:
traceback.print_exc() traceback.print_exc()
def _check_arguments(self): 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 input is a file
if self.input_path.is_file(): elif self.input.is_file():
if self.output_path.is_dir(): if self.output.is_dir():
Avalon.error(_('Input and output path type mismatch')) Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory')) Avalon.error(_('Input is single file but output is directory'))
raise ArgumentError('input output path type mismatch') 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(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg')) Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified') raise ArgumentError('no output video suffix specified')
# if input is a directory # if input is a directory
elif self.input_path.is_dir(): elif self.input.is_dir():
if self.output_path.is_file(): if self.output.is_file():
Avalon.error(_('Input and output path type mismatch')) Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is directory but output is existing single file')) Avalon.error(_('Input is directory but output is existing single file'))
raise ArgumentError('input output path type mismatch') raise ArgumentError('input output path type mismatch')
@ -155,7 +163,7 @@ class Upscaler:
# if input is neither # if input is neither
else: else:
Avalon.error(_('Input path is neither a file nor a directory')) 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 # check Fmpeg settings
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path']) ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
@ -259,32 +267,32 @@ class Upscaler:
# if the driver being used is waifu2x-caffe # if the driver being used is waifu2x-caffe
if self.driver == 'waifu2x_caffe': if self.driver == 'waifu2x_caffe':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio, self.scale_ratio,
self.scale_width, self.scale_width,
self.scale_height, self.scale_height,
self.image_format, self.image_format,
self.bit_depth)) self.bit_depth))
# if the driver being used is waifu2x-converter-cpp # if the driver being used is waifu2x-converter-cpp
elif self.driver == 'waifu2x_converter_cpp': elif self.driver == 'waifu2x_converter_cpp':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio, self.scale_ratio,
self.processes, self.processes,
self.image_format)) self.image_format))
# if the driver being used is waifu2x-ncnn-vulkan # if the driver being used is waifu2x-ncnn-vulkan
elif self.driver == 'waifu2x_ncnn_vulkan': elif self.driver == 'waifu2x_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio)) self.scale_ratio))
# if the driver being used is srmd_ncnn_vulkan # if the driver being used is srmd_ncnn_vulkan
elif self.driver == 'srmd_ncnn_vulkan': elif self.driver == 'srmd_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio)) self.scale_ratio))
# start progress bar in a different thread # start progress bar in a different thread
Avalon.debug_info(_('Starting progress monitor')) Avalon.debug_info(_('Starting progress monitor'))
@ -390,18 +398,34 @@ class Upscaler:
# define processing queue # define processing queue
processing_queue = queue.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 input specified is single file
if self.input_path.is_file(): elif self.input.is_file():
Avalon.info(_('Upscaling single video file: {}').format(self.input_path)) Avalon.info(_('Upscaling single video file: {}').format(self.input))
processing_queue.put((self.input_path.absolute(), self.output_path.absolute())) processing_queue.put((self.input.absolute(), self.output.absolute()))
# if input specified is a directory # 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 # make output directory if it doesn't exist
self.output_path.mkdir(parents=True, exist_ok=True) self.output.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in self.input_path.iterdir() if f.is_file()]: for input_video in [f for f in self.input.iterdir() if f.is_file()]:
output_video = self.output_path / input_video.name output_video = self.output / input_video.name
processing_queue.put((input_video.absolute(), output_video.absolute())) processing_queue.put((input_video.absolute(), output_video.absolute()))
while not processing_queue.empty(): while not processing_queue.empty():

View File

@ -4,7 +4,7 @@
Creator: Video2X GUI Creator: Video2X GUI
Author: K4YT3X Author: K4YT3X
Date Created: May 5, 2020 Date Created: May 5, 2020
Last Modified: May 7, 2020 Last Modified: May 8, 2020
""" """
# local imports # local imports
@ -14,7 +14,6 @@ from upscaler import Upscaler
import contextlib import contextlib
import os import os
import pathlib import pathlib
import re
import sys import sys
import tempfile import tempfile
import time import time
@ -22,9 +21,10 @@ import traceback
import yaml import yaml
# third-party imports # third-party imports
from PyQt5 import QtWidgets, QtGui from PyQt5 import QtGui, uic
from PyQt5 import uic from PyQt5.QtCore import *
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool from PyQt5.QtWidgets import *
# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
VERSION = '2.0.0' VERSION = '2.0.0'
@ -42,6 +42,7 @@ AVAILABLE_DRIVERS = {
'Anime4KCPP': 'anime4kcpp' 'Anime4KCPP': 'anime4kcpp'
} }
def resource_path(relative_path: str) -> pathlib.Path: def resource_path(relative_path: str) -> pathlib.Path:
try: try:
base_path = pathlib.Path(sys._MEIPASS) base_path = pathlib.Path(sys._MEIPASS)
@ -56,6 +57,7 @@ class WorkerSignals(QObject):
interrupted = pyqtSignal() interrupted = pyqtSignal()
finished = pyqtSignal() finished = pyqtSignal()
class ProgressBarWorker(QRunnable): class ProgressBarWorker(QRunnable):
def __init__(self, fn, *args, **kwargs): def __init__(self, fn, *args, **kwargs):
super(ProgressBarWorker, self).__init__() super(ProgressBarWorker, self).__init__()
@ -72,6 +74,7 @@ class ProgressBarWorker(QRunnable):
except Exception: except Exception:
pass pass
class UpscalerWorker(QRunnable): class UpscalerWorker(QRunnable):
def __init__(self, fn, *args, **kwargs): def __init__(self, fn, *args, **kwargs):
@ -99,7 +102,50 @@ class UpscalerWorker(QRunnable):
self.signals.finished.emit() 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): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -114,131 +160,147 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path)) self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
# menu bar # 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_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))) self.action_about.triggered.connect(lambda: self.show_message(LEGAL_INFO, custom_icon=QtGui.QPixmap(self.video2x_icon_path)))
# main tab # main tab
# select input file/folder # select input file/folder
self.input_line_edit = self.findChild(QtWidgets.QLineEdit, 'inputLineEdit') self.input_table_view = self.findChild(QTableView, 'inputTableView')
self.enable_line_edit_file_drop(self.input_line_edit) self.input_table_view.dragEnterEvent = self.dragEnterEvent
self.input_select_file_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFileButton') self.input_table_view.dropEvent = self.dropEvent
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)
# 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 # 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.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_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) self.output_select_folder_button.clicked.connect(self.select_output_folder)
# config file # 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.enable_line_edit_file_drop(self.config_line_edit)
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute())) 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) self.config_select_file_button.clicked.connect(self.select_config_file)
# cache directory # 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.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) self.cache_select_folder_button.clicked.connect(self.select_cache_folder)
# express settings # 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.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
self.processes_spin_box = self.findChild(QtWidgets.QSpinBox, 'processesSpinBox') self.processes_spin_box = self.findChild(QSpinBox, 'processesSpinBox')
self.scale_ratio_double_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'scaleRatioDoubleSpinBox') self.scale_ratio_double_spin_box = self.findChild(QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
self.preserve_frames_check_box = self.findChild(QtWidgets.QCheckBox, 'preserveFramesCheckBox') self.preserve_frames_check_box = self.findChild(QCheckBox, 'preserveFramesCheckBox')
# progress bar and start/stop controls # progress bar and start/stop controls
self.progress_bar = self.findChild(QtWidgets.QProgressBar, 'progressBar') self.progress_bar = self.findChild(QProgressBar, 'progressBar')
self.time_elapsed_label = self.findChild(QtWidgets.QLabel, 'timeElapsedLabel') self.time_elapsed_label = self.findChild(QLabel, 'timeElapsedLabel')
self.time_remaining_label = self.findChild(QtWidgets.QLabel, 'timeRemainingLabel') self.time_remaining_label = self.findChild(QLabel, 'timeRemainingLabel')
self.rate_label = self.findChild(QtWidgets.QLabel, 'rateLabel') self.rate_label = self.findChild(QLabel, 'rateLabel')
self.start_button = self.findChild(QtWidgets.QPushButton, 'startButton') self.start_button = self.findChild(QPushButton, 'startButton')
self.start_button.clicked.connect(self.start) 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) self.stop_button.clicked.connect(self.stop)
# driver settings # driver settings
# waifu2x-caffe # 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.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_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_mode_combo_box = self.findChild(QComboBox, 'waifu2xCaffeModeComboBox')
self.waifu2x_caffe_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox') self.waifu2x_caffe_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
self.waifu2x_caffe_process_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeProcessComboBox') self.waifu2x_caffe_process_combo_box = self.findChild(QComboBox, 'waifu2xCaffeProcessComboBox')
self.waifu2x_caffe_model_combobox = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModelComboBox') self.waifu2x_caffe_model_combobox = self.findChild(QComboBox, 'waifu2xCaffeModelComboBox')
self.waifu2x_caffe_crop_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeCropSizeSpinBox') self.waifu2x_caffe_crop_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeCropSizeSpinBox')
self.waifu2x_caffe_output_quality_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputQualitySpinBox') self.waifu2x_caffe_output_quality_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputQualitySpinBox')
self.waifu2x_caffe_output_depth_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputDepthSpinBox') self.waifu2x_caffe_output_depth_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputDepthSpinBox')
self.waifu2x_caffe_batch_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeBatchSizeSpinBox') self.waifu2x_caffe_batch_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeBatchSizeSpinBox')
self.waifu2x_caffe_gpu_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeGpuSpinBox') self.waifu2x_caffe_gpu_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeGpuSpinBox')
self.waifu2x_caffe_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xCaffeTtaCheckBox') self.waifu2x_caffe_tta_check_box = self.findChild(QCheckBox, 'waifu2xCaffeTtaCheckBox')
# waifu2x-converter-cpp # 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.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_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_png_compression_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppProcessorSpinBox') self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
self.waifu2x_converter_cpp_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModelComboBox') self.waifu2x_converter_cpp_model_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModelComboBox')
self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModeComboBox') self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModeComboBox')
self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QtWidgets.QCheckBox, 'disableGpuCheckBox') self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QCheckBox, 'disableGpuCheckBox')
self.waifu2x_converter_cpp_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'ttaCheckBox') self.waifu2x_converter_cpp_tta_check_box = self.findChild(QCheckBox, 'ttaCheckBox')
# waifu2x-ncnn-vulkan # 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.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_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_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox')
self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox') self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox')
self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xNcnnVulkanModelComboBox') self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'waifu2xNcnnVulkanModelComboBox')
self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox') self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox')
self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit') self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit')
self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox') self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox')
# srmd-ncnn-vulkan # 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.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_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_noise_level_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox')
self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox') self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox')
self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'srmdNcnnVulkanModelComboBox') self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'srmdNcnnVulkanModelComboBox')
self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox') self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox')
self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanJobsLineEdit') self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'srmdNcnnVulkanTtaCheckBox') self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# anime4k # 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.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_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_passes_spin_box = self.findChild(QSpinBox, 'anime4kCppPassesSpinBox')
self.anime4kcpp_push_color_count_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPushColorCountSpinBox') self.anime4kcpp_push_color_count_spin_box = self.findChild(QSpinBox, 'anime4kCppPushColorCountSpinBox')
self.anime4kcpp_strength_color_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox') self.anime4kcpp_strength_color_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox')
self.anime4kcpp_strength_gradient_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox') self.anime4kcpp_strength_gradient_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox')
self.anime4kcpp_threads_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppThreadsSpinBox') self.anime4kcpp_threads_spin_box = self.findChild(QSpinBox, 'anime4kCppThreadsSpinBox')
self.anime4kcpp_pre_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPreFiltersSpinBox') self.anime4kcpp_pre_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPreFiltersSpinBox')
self.anime4kcpp_post_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPostFiltersSpinBox') self.anime4kcpp_post_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPostFiltersSpinBox')
self.anime4kcpp_platform_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPlatformIdSpinBox') self.anime4kcpp_platform_id_spin_box = self.findChild(QSpinBox, 'anime4kCppPlatformIdSpinBox')
self.anime4kcpp_device_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppDeviceIdSpinBox') self.anime4kcpp_device_id_spin_box = self.findChild(QSpinBox, 'anime4kCppDeviceIdSpinBox')
self.anime4kcpp_codec_combo_box = self.findChild(QtWidgets.QComboBox, 'anime4kCppCodecComboBox') self.anime4kcpp_codec_combo_box = self.findChild(QComboBox, 'anime4kCppCodecComboBox')
self.anime4kcpp_fast_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppFastModeCheckBox') self.anime4kcpp_fast_mode_check_box = self.findChild(QCheckBox, 'anime4kCppFastModeCheckBox')
self.anime4kcpp_pre_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPreProcessingCheckBox') self.anime4kcpp_pre_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPreProcessingCheckBox')
self.anime4kcpp_post_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPostProcessingCheckBox') self.anime4kcpp_post_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPostProcessingCheckBox')
self.anime4kcpp_gpu_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppGpuModeCheckBox') self.anime4kcpp_gpu_mode_check_box = self.findChild(QCheckBox, 'anime4kCppGpuModeCheckBox')
# load configurations # load configurations
self.load_configurations() self.load_configurations()
@ -250,11 +312,15 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
event.ignore() event.ignore()
def dropEvent(self, event): def dropEvent(self, event):
input_path = pathlib.Path(event.mimeData().urls()[0].toLocalFile()) input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
self.input_line_edit.setText(str(input_path.absolute())) for path in input_paths:
self.generate_output_path(input_path) 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.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute())) 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 file doesn't exist, return
if not config_file_path.is_file(): 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 return
# read configuration dict from config file # read configuration dict from config file
@ -438,19 +504,50 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
else: else:
self.processes_spin_box.setValue(1) 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: 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] == '': if not isinstance(file_selected, tuple) or file_selected[0] == '':
return None return None
return pathlib.Path(file_selected[0]) return pathlib.Path(file_selected[0])
def select_folder(self, *args, **kwargs) -> pathlib.Path: 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 == '': if folder_selected == '':
return None return None
return pathlib.Path(folder_selected) 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 # 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()): if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return return
@ -466,23 +563,27 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
if input_path.is_file(): if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4') output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4')
elif input_path.is_dir(): 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 output_path_id += 1
if not output_path.exists(): if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute())) self.output_line_edit.setText(str(output_path.absolute()))
def select_input_file(self): 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 return
self.generate_output_path(input_file) self.input_table_data.append(input_file)
self.input_line_edit.setText(str(input_file.absolute())) self.update_output_path()
self.update_input_table()
def select_input_folder(self): 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 return
self.generate_output_path(input_folder) self.input_table_data.append(input_folder)
self.input_line_edit.setText(str(input_folder.absolute())) self.update_output_path()
self.update_input_table()
def select_output_file(self): def select_output_file(self):
if (output_file := self.select_file('Select Output File')) is None: 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())) driver_line_edit.setText(str(driver_binary_path.absolute()))
def show_error(self, message: str): def show_error(self, message: str):
QtWidgets.QErrorMessage(self).showMessage(message.replace('\n', '<br>')) QErrorMessage(self).showMessage(message.replace('\n', '<br>'))
def show_message(self, message: str, custom_icon=None): def show_message(self, message: str, custom_icon=None):
message_box = QtWidgets.QMessageBox() message_box = QMessageBox()
message_box.setWindowTitle('Message') message_box.setWindowTitle('Message')
if custom_icon: if custom_icon:
message_box.setIconPixmap(custom_icon.scaled(64, 64)) message_box.setIconPixmap(custom_icon.scaled(64, 64))
else: else:
message_box.setIcon(QtWidgets.QMessageBox.Information) message_box.setIcon(QMessageBox.Information)
message_box.setText(message) message_box.setText(message)
message_box.exec_() message_box.exec_()
@ -542,9 +643,9 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
progress_percentage = 0 progress_percentage = 0
progress_callback.emit((progress_percentage, progress_callback.emit((progress_percentage,
self.upscaler.total_frames_upscaled, self.upscaler.total_frames_upscaled,
self.upscaler.total_frames, self.upscaler.total_frames,
upscale_begin_time)) upscale_begin_time))
time.sleep(1) time.sleep(1)
# upscale process will stop at 99% # upscale process will stop at 99%
@ -580,14 +681,17 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.begin_time = time.time() self.begin_time = time.time()
# resolve input and output directories from GUI # resolve input and output directories from GUI
if self.input_line_edit.text().strip() == '': if len(self.input_table_data) == 0:
self.show_error('Input path not specified') self.show_error('Input path unspecified')
return return
if self.output_line_edit.text().strip() == '': if self.output_line_edit.text().strip() == '':
self.show_error('Output path not specified') self.show_error('Output path unspecified')
return 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())) output_directory = pathlib.Path(os.path.expandvars(self.output_line_edit.text()))
# load driver settings from GUI # load driver settings from GUI
@ -658,7 +762,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
# this file shouldn't be imported # this file shouldn't be imported
if __name__ == '__main__': if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv) app = QApplication(sys.argv)
window = Video2XMainWindow() window = Video2XMainWindow()
window.show() window.show()
app.exec_() app.exec_()

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>691</width> <width>718</width>
<height>503</height> <height>668</height>
</rect> </rect>
</property> </property>
<property name="acceptDrops"> <property name="acceptDrops">
@ -16,7 +16,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Video2X GUI</string> <string>Video2X GUI</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>5</number>
@ -79,154 +79,160 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="fileSelectionVerticalLayout"> <widget class="QGroupBox" name="inputSelectionGroupBox">
<item> <property name="title">
<layout class="QHBoxLayout" name="inputHorizontalLayout"> <string>Input Selection</string>
<item> </property>
<widget class="QLabel" name="inputLabel"> <layout class="QVBoxLayout" name="verticalLayout_5">
<property name="minimumSize"> <item>
<size> <widget class="QTableView" name="inputTableView"/>
<width>63</width> </item>
<height>0</height> <item>
</size> <layout class="QHBoxLayout" name="inputButtonsHorizontalLayout">
</property> <item>
<property name="lineWidth"> <widget class="QPushButton" name="inputSelectFileButton">
<number>1</number> <property name="enabled">
</property> <bool>true</bool>
<property name="text"> </property>
<string>Input</string> <property name="text">
</property> <string>Select File</string>
<property name="wordWrap"> </property>
<bool>false</bool> </widget>
</property> </item>
<property name="margin"> <item>
<number>0</number> <widget class="QPushButton" name="inputSelectFolderButton">
</property> <property name="text">
</widget> <string>Select Folder</string>
</item> </property>
<item> </widget>
<widget class="QLineEdit" name="inputLineEdit"/> </item>
</item> <item>
<item> <widget class="QPushButton" name="inputDeleteSelectedButton">
<widget class="QPushButton" name="inputSelectFileButton"> <property name="text">
<property name="enabled"> <string>Delete Selected</string>
<bool>true</bool> </property>
</property> </widget>
<property name="text"> </item>
<string>Select File</string> <item>
</property> <widget class="QPushButton" name="inputClearAllButton">
</widget> <property name="text">
</item> <string>Clear All</string>
<item> </property>
<widget class="QPushButton" name="inputSelectFolderButton"> </widget>
<property name="text"> </item>
<string>Select Folder</string> </layout>
</property> </item>
</widget> </layout>
</item> </widget>
</layout> </item>
</item> <item>
<item> <widget class="QGroupBox" name="otherPathsSelectionGroupBox">
<layout class="QHBoxLayout" name="outputHorizontalLayout"> <property name="title">
<item> <string>Other Paths Selection</string>
<widget class="QLabel" name="outputLabel"> </property>
<property name="sizePolicy"> <layout class="QVBoxLayout" name="verticalLayout_9">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <item>
<horstretch>0</horstretch> <layout class="QHBoxLayout" name="outputHorizontalLayout">
<verstretch>0</verstretch> <item>
</sizepolicy> <widget class="QLabel" name="outputLabel">
</property> <property name="sizePolicy">
<property name="minimumSize"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<size> <horstretch>0</horstretch>
<width>63</width> <verstretch>0</verstretch>
<height>0</height> </sizepolicy>
</size> </property>
</property> <property name="minimumSize">
<property name="text"> <size>
<string>Output</string> <width>63</width>
</property> <height>0</height>
</widget> </size>
</item> </property>
<item> <property name="text">
<widget class="QLineEdit" name="outputLineEdit"/> <string>Output</string>
</item> </property>
<item> </widget>
<widget class="QPushButton" name="outputSelectFileButton"> </item>
<property name="text"> <item>
<string>Select File</string> <widget class="QLineEdit" name="outputLineEdit"/>
</property> </item>
</widget> <item>
</item> <widget class="QPushButton" name="outputSelectFileButton">
<item> <property name="text">
<widget class="QPushButton" name="outputSelectFolderButton"> <string>Select File</string>
<property name="text"> </property>
<string>Select Folder</string> </widget>
</property> </item>
</widget> <item>
</item> <widget class="QPushButton" name="outputSelectFolderButton">
</layout> <property name="text">
</item> <string>Select Folder</string>
<item> </property>
<layout class="QHBoxLayout" name="configHorizontalLayout"> </widget>
<item> </item>
<widget class="QLabel" name="configLabel"> </layout>
<property name="sizePolicy"> </item>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <item>
<horstretch>0</horstretch> <layout class="QHBoxLayout" name="configHorizontalLayout">
<verstretch>0</verstretch> <item>
</sizepolicy> <widget class="QLabel" name="configLabel">
</property> <property name="sizePolicy">
<property name="minimumSize"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<size> <horstretch>0</horstretch>
<width>63</width> <verstretch>0</verstretch>
<height>0</height> </sizepolicy>
</size> </property>
</property> <property name="minimumSize">
<property name="text"> <size>
<string>Config</string> <width>63</width>
</property> <height>0</height>
</widget> </size>
</item> </property>
<item> <property name="text">
<widget class="QLineEdit" name="configLineEdit"/> <string>Config</string>
</item> </property>
<item> </widget>
<widget class="QPushButton" name="configSelectButton"> </item>
<property name="text"> <item>
<string>Select</string> <widget class="QLineEdit" name="configLineEdit"/>
</property> </item>
</widget> <item>
</item> <widget class="QPushButton" name="configSelectButton">
</layout> <property name="text">
</item> <string>Select</string>
<item> </property>
<layout class="QHBoxLayout" name="cacheHorizontalLayout"> </widget>
<item> </item>
<widget class="QLabel" name="cacheLabel"> </layout>
<property name="minimumSize"> </item>
<size> <item>
<width>63</width> <layout class="QHBoxLayout" name="cacheHorizontalLayout">
<height>0</height> <item>
</size> <widget class="QLabel" name="cacheLabel">
</property> <property name="minimumSize">
<property name="text"> <size>
<string>Cache Folder</string> <width>63</width>
</property> <height>0</height>
</widget> </size>
</item> </property>
<item> <property name="text">
<widget class="QLineEdit" name="cacheLineEdit"/> <string>Cache Folder</string>
</item> </property>
<item> </widget>
<widget class="QPushButton" name="cacheSelectFolderButton"> </item>
<property name="text"> <item>
<string>Select Folder</string> <widget class="QLineEdit" name="cacheLineEdit"/>
</property> </item>
</widget> <item>
</item> <widget class="QPushButton" name="cacheSelectFolderButton">
</layout> <property name="text">
</item> <string>Select Folder</string>
</layout> </property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="expressSettingsGroupBox"> <widget class="QGroupBox" name="expressSettingsGroupBox">
@ -1344,7 +1350,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>691</width> <width>718</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>