File indexing completed on 2024-06-23 04:27:59
0001 """ 0002 GDQuest Batch Exporter 0003 ----------------- 0004 Batch export art assets from Krita using layer metadata. 0005 Updates and reads metadata in Krita's layer names, and uses it to smartly process and export layers. 0006 Export to the Blender Cut-Out Animation tools for modular 2d game animation. 0007 SPDX-License-Identifier: GPL-3.0-only 0008 """ 0009 0010 from functools import partial 0011 from krita import DockWidget, DockWidgetFactory, DockWidgetFactoryBase, Krita 0012 from PyQt5.QtWidgets import ( 0013 QPushButton, 0014 QStatusBar, 0015 QLabel, 0016 QLineEdit, 0017 QHBoxLayout, 0018 QVBoxLayout, 0019 QGroupBox, 0020 QWidget, 0021 ) 0022 import os 0023 from .Config import CONFIG 0024 from .Infrastructure import WNode 0025 from .COATools import COAToolsFormat 0026 from .Utils import kickstart, flip 0027 from .Utils.Tree import iterPre 0028 0029 KI = Krita.instance() 0030 0031 0032 def ensureRGBAU8(doc): 0033 ensured = doc.colorModel() == "RGBA" and doc.colorDepth() == "U8" 0034 if not ensured: 0035 raise ValueError("only RGBA 8-bit depth supported!") 0036 0037 0038 def exportAllLayers(cfg, statusBar): 0039 msg, timeout = (cfg["done"]["msg"].format("Exported all layers."), cfg["done"]["timeout"]) 0040 try: 0041 doc = KI.activeDocument() 0042 0043 root = doc.rootNode() 0044 root = WNode(cfg, root) 0045 0046 dirName = os.path.dirname(doc.fileName()) 0047 it = filter(lambda n: n.isExportable() and n.isMarked(), iterPre(root)) 0048 it = map(partial(flip(WNode.save), dirName), it) 0049 kickstart(it) 0050 except ValueError as e: 0051 msg, timeout = cfg["error"]["msg"].format(e), cfg["error"]["timeout"] 0052 statusBar.showMessage(msg, timeout) 0053 0054 0055 def exportSelectedLayers(cfg, statusBar): 0056 msg, timeout = (cfg["done"]["msg"].format("Exported selected layers."), cfg["done"]["timeout"]) 0057 try: 0058 doc = KI.activeDocument() 0059 0060 dirName = os.path.dirname(doc.fileName()) 0061 nodes = KI.activeWindow().activeView().selectedNodes() 0062 it = map(partial(WNode, cfg), nodes) 0063 it = map(partial(flip(WNode.save), dirName), it) 0064 kickstart(it) 0065 except ValueError as e: 0066 msg, timeout = cfg["error"]["msg"].format(e), cfg["error"]["timeout"] 0067 statusBar.showMessage(msg, timeout) 0068 0069 0070 def exportCOATools(mode, cfg, statusBar): 0071 msg, timeout = ( 0072 cfg["done"]["msg"].format("Exported %s layers to COA Tools format." % (mode)), 0073 cfg["done"]["timeout"], 0074 ) 0075 try: 0076 doc = KI.activeDocument() 0077 ensureRGBAU8(doc) 0078 0079 coat_format = COAToolsFormat(cfg, statusBar) 0080 dirName = os.path.dirname(doc.fileName()) 0081 nodes = KI.activeWindow().activeView().selectedNodes() 0082 0083 # If mode is document or no nodes are selected, use document root 0084 if mode == "document" or len(nodes) == 0: 0085 nodes = [doc.rootNode()] 0086 0087 it = map(partial(WNode, cfg), nodes) 0088 # By convention all selected nodes should be Group Layers 0089 # This is to represent a logical root for each export in COATools format 0090 it = filter(lambda n: n.isGroupLayer(), it) 0091 it = map(coat_format.collect, it) 0092 kickstart(it) 0093 coat_format.save(dirName) 0094 0095 except ValueError as e: 0096 msg, timeout = cfg["error"]["msg"].format(e), cfg["error"]["timeout"] 0097 statusBar.showMessage(msg, timeout) 0098 0099 0100 def renameLayers(cfg, statusBar, lineEdit): 0101 msg, timeout = (cfg["done"]["msg"].format("Renaming successful!"), cfg["done"]["timeout"]) 0102 try: 0103 nodes = KI.activeWindow().activeView().selectedNodes() 0104 it = map(partial(WNode, cfg), nodes) 0105 it = map(partial(flip(WNode.rename), lineEdit.text()), it) 0106 kickstart(it) 0107 except ValueError as e: 0108 msg, timeout = cfg["error"]["msg"].format(e), cfg["error"]["timeout"] 0109 statusBar.showMessage(msg, timeout) 0110 0111 0112 class GameArtTools(DockWidget): 0113 title = "Batch Exporter" 0114 0115 def __init__(self): 0116 super().__init__() 0117 KI.setBatchmode(True) 0118 self.setWindowTitle(self.title) 0119 self.createInterface() 0120 0121 def createInterface(self): 0122 uiContainer = QWidget(self) 0123 0124 exportLabel = QLabel("Export") 0125 exportAllLayersButton = QPushButton("All Layers") 0126 exportSelectedLayersButton = QPushButton("Selected Layers") 0127 renameLabel = QLabel("Update Name and Metadata") 0128 renameLineEdit = QLineEdit() 0129 renameButton = QPushButton() 0130 renameButton.setIcon(KI.icon("view-refresh")) 0131 statusBar = QStatusBar() 0132 0133 exportLabel.setToolTip("Export individual images") 0134 exportAllLayersButton.setToolTip("Export all layers with metadata") 0135 exportSelectedLayersButton.setToolTip("Export selected layers only") 0136 renameButton.setToolTip("Batch update selected layer names and metadata") 0137 0138 # COA Tools GroupBox 0139 coaToolsGroupBox = QGroupBox("COA Tools") 0140 coaToolsHBoxLayout = QHBoxLayout() 0141 coaToolsExportSelectedLayersButton = QPushButton("Selected Layers") 0142 coaToolsExportDocumentButton = QPushButton("Document") 0143 0144 coaToolsGroupBox.setToolTip("Blender Cut-Out Animation Tools") 0145 coaToolsExportSelectedLayersButton.setToolTip("Export selected layers only") 0146 coaToolsExportDocumentButton.setToolTip("Export all layers with metadata") 0147 0148 coaToolsHBoxLayout.addWidget(coaToolsExportDocumentButton) 0149 coaToolsHBoxLayout.addWidget(coaToolsExportSelectedLayersButton) 0150 coaToolsGroupBox.setLayout(coaToolsHBoxLayout) 0151 0152 vboxlayout = QVBoxLayout() 0153 vboxlayout.addWidget(exportLabel) 0154 0155 vboxlayout.addWidget(exportAllLayersButton) 0156 vboxlayout.addWidget(exportSelectedLayersButton) 0157 0158 vboxlayout.addWidget(coaToolsGroupBox) 0159 vboxlayout.addWidget(renameLabel) 0160 0161 hboxlayout = QHBoxLayout() 0162 hboxlayout.addWidget(renameLineEdit) 0163 hboxlayout.addWidget(renameButton) 0164 0165 vboxlayout.addLayout(hboxlayout) 0166 vboxlayout.addStretch() 0167 vboxlayout.addWidget(statusBar) 0168 0169 uiContainer.setLayout(vboxlayout) 0170 self.setWidget(uiContainer) 0171 0172 exportSelectedLayersButton.released.connect( 0173 partial(exportSelectedLayers, CONFIG, statusBar) 0174 ) 0175 exportAllLayersButton.released.connect(partial(exportAllLayers, CONFIG, statusBar)) 0176 coaToolsExportSelectedLayersButton.released.connect( 0177 partial(exportCOATools, "selected", CONFIG, statusBar) 0178 ) 0179 coaToolsExportDocumentButton.released.connect( 0180 partial(exportCOATools, "document", CONFIG, statusBar) 0181 ) 0182 renameLineEdit.returnPressed.connect( 0183 partial(renameLayers, CONFIG, statusBar, renameLineEdit) 0184 ) 0185 renameButton.released.connect(partial(renameLayers, CONFIG, statusBar, renameLineEdit)) 0186 0187 def canvasChanged(self, canvas): 0188 pass 0189 0190 0191 def registerDocker(): 0192 docker = DockWidgetFactory( 0193 "pykrita_gdquest_art_tools", DockWidgetFactoryBase.DockRight, GameArtTools 0194 ) 0195 KI.addDockWidgetFactory(docker)