This repository has been archived on 2022-04-04. You can view files and clone it, but cannot push or open issues or pull requests.
2022-03-29 12:42:59 -04:00

201 lines
8.3 KiB
Python

"""anim_stitcher GUI"""
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
import pkgutil
from qt_common import qt_window_center, qt_lines, qt_dialog_style
import sprite_types
import re
import os
from . import stitcher
from project_root import get_project_root
import logging
logger = logging.getLogger("anim_stitcher.ui")
SPRITE_NAMING_VALIDATION = r"^[a-z][a-zA-Z\d]+$"
class AnimStitcherWindow(QtWidgets.QWidget):
selected_files = None
def __init__(self):
super(AnimStitcherWindow, self).__init__()
# Configure the window
self.setWindowFlags(
self.windowFlags() ^ Qt.Window)
self.setWindowTitle("Anim Stitcher")
self.resize(280, 200)
qt_window_center.center_window(self)
# Set the root of the application to be a vertical list
self.setLayout(QtWidgets.QVBoxLayout())
# Load the stylesheet for this app
self.setStyleSheet(qt_dialog_style.STYLESHEET)
# Configure the title at the top of the window
self.label = QtWidgets.QLabel("Anim Stitcher")
self.label.setProperty('labelClass', 'label-title')
self.layout().addWidget(self.label)
self.description = QtWidgets.QLabel(
"Stitch PNG sequences into a sprite sheet")
self.description.setProperty('labelClass', 'label-description')
self.layout().addWidget(self.description)
self.layout().addWidget(qt_lines.QHLine())
# Add an import button
self.import_button = QtWidgets.QPushButton("Select PNGs")
self.import_button.clicked.connect(self.load_png_dialog)
self.layout().addWidget(self.import_button)
self.layout().addWidget(qt_lines.QHLine())
# Add a selection option for the sprite type
known_sprite_types = sprite_types.get_known_sprite_types().values()
self.sprite_type_layout = QtWidgets.QHBoxLayout()
self.sprite_type_label = QtWidgets.QLabel("Sprite Type")
self.sprite_type_layout.addWidget(self.sprite_type_label)
self.sprite_type_dropdown = QtWidgets.QComboBox()
for ty in known_sprite_types:
self.sprite_type_dropdown.addItem(ty)
self.sprite_type_dropdown.setEnabled(False)
self.sprite_type_layout.addWidget(self.sprite_type_dropdown)
self.layout().addLayout(self.sprite_type_layout)
# Add a box to accept a sprite name
self.sprite_name_layout = QtWidgets.QHBoxLayout()
self.sprite_name_label = QtWidgets.QLabel("Sprite Name")
self.sprite_name_layout.addWidget(self.sprite_name_label)
self.sprite_name_input = QtWidgets.QLineEdit()
self.sprite_name_input.setText("unnamedSprite")
self.sprite_name_input.setEnabled(False)
self.sprite_name_layout.addWidget(self.sprite_name_input)
self.layout().addLayout(self.sprite_name_layout)
# Add a selection option for the sprite optimization
self.optimization_layout = QtWidgets.QHBoxLayout()
self.optimization_label = QtWidgets.QLabel("Optimize For")
self.optimization_layout.addWidget(self.optimization_label)
self.optimization_dropdown = QtWidgets.QComboBox()
self.optimization_dropdown.addItem("Size")
self.optimization_dropdown.addItem("Quality")
self.optimization_dropdown.setEnabled(False)
self.optimization_layout.addWidget(self.optimization_dropdown)
self.layout().addLayout(self.optimization_layout)
# Add a number input for the target FPS
self.fps_layout = QtWidgets.QHBoxLayout()
self.fps_label = QtWidgets.QLabel("Target FPS")
self.fps_layout.addWidget(self.fps_label)
self.fps_input = QtWidgets.QLineEdit()
self.fps_input.setText("24")
self.fps_input.setEnabled(False)
self.fps_layout.addWidget(self.fps_input)
self.layout().addLayout(self.fps_layout)
# Add a seperator
self.layout().addWidget(qt_lines.QHLine())
# Add a button to start the stitching process
self.finishing_layout = QtWidgets.QHBoxLayout()
self.close_button = QtWidgets.QPushButton("Cancel")
self.close_button.clicked.connect(self.close)
self.finishing_layout.addWidget(self.close_button)
self.stitch_button = QtWidgets.QPushButton("Stitch")
self.stitch_button.clicked.connect(self.stitch_images)
self.stitch_button.setEnabled(False)
self.finishing_layout.addWidget(self.stitch_button)
self.layout().addLayout(self.finishing_layout)
# Add space at the bottom in case window size is wrong
self.layout().addStretch()
def load_png_dialog(self):
# Open a file picker to search for the desired image
file_dialog = QtWidgets.QFileDialog()
file_dialog.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
file_dialog.setNameFilter("Image Files (*.png *.jpg *.jpeg)")
file_dialog.setViewMode(QtWidgets.QFileDialog.Detail)
file_dialog.setLabelText(QtWidgets.QFileDialog.Accept, "Import")
file_dialog.setLabelText(QtWidgets.QFileDialog.Reject, "Cancel")
file_dialog.setWindowTitle("Import PNG Sequence")
file_dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
file_dialog.setDirectory(os.path.join(get_project_root(), "assets"))
# If the user selected an image, import it
if file_dialog.exec_():
# Enable all the disabled fields
self.sprite_type_dropdown.setEnabled(True)
self.sprite_name_input.setEnabled(True)
self.stitch_button.setEnabled(True)
self.optimization_dropdown.setEnabled(True)
self.fps_input.setEnabled(True)
# Save the selected files
self.selected_files = file_dialog.selectedFiles()
else:
logger.warning("No image selected")
return
def stitch_images(self):
# Check the naming convention
if not re.match(SPRITE_NAMING_VALIDATION, self.sprite_name_input.text()):
# Pop up a warning
warning_dialog = QtWidgets.QMessageBox()
warning_dialog.setIcon(QtWidgets.QMessageBox.Warning)
warning_dialog.setText("Invalid Sprite Name")
warning_dialog.setInformativeText(
"The sprite name must be lower camel case\nExample: myShinySprite")
warning_dialog.setWindowTitle("Invalid Sprite Name")
warning_dialog.exec_()
return
# Check if we are about to overwrite an existing sprite
known_sprite_types = sprite_types.get_known_sprite_types()
ty_long_to_short_map = dict(map(reversed, known_sprite_types.items()))
sprite_type = ty_long_to_short_map[self.sprite_type_dropdown.currentText(
)]
sprite_name = self.sprite_name_input.text()
if stitcher.check_sprite_exists(sprite_type, sprite_name):
# Pop up confirmation box
warning_dialog = QtWidgets.QMessageBox()
warning_dialog.setIcon(QtWidgets.QMessageBox.Warning)
warning_dialog.setText("Overwrite Sprite?")
warning_dialog.setInformativeText(
"A sprite with the name {}_{} already exists.\nDo you want to overwrite it?".format(sprite_type, sprite_name))
warning_dialog.setWindowTitle("Overwrite Sprite?")
warning_dialog.setStandardButtons(
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
warning_dialog.setDefaultButton(QtWidgets.QMessageBox.No)
if warning_dialog.exec_() == QtWidgets.QMessageBox.No:
return
# Pop up an error if the inputted FPS is not a number
try:
fps = float(self.fps_input.text())
except ValueError:
warning_dialog = QtWidgets.QMessageBox()
warning_dialog.setIcon(QtWidgets.QMessageBox.Warning)
warning_dialog.setText("Invalid FPS")
warning_dialog.setInformativeText(
"The FPS must be a number")
warning_dialog.setWindowTitle("Invalid FPS")
warning_dialog.exec_()
return
# Perform the actual stitching action
stitcher.stitch_images_and_write_to_disk(
sprite_type, sprite_name, self.selected_files, self.optimization_dropdown.currentText() == "Size", float(self.fps_input.text()))
# Close the window
self.close()