File indexing completed on 2024-06-16 04:17:40

0001 # SPDX-License-Identifier: CC0-1.0
0002 
0003 from . import exportlayersdialog
0004 from PyQt5.QtCore import (Qt, QRect)
0005 from PyQt5.QtWidgets import (QFormLayout, QListWidget, QHBoxLayout,
0006                              QDialogButtonBox, QVBoxLayout, QFrame,
0007                              QPushButton, QAbstractScrollArea, QLineEdit,
0008                              QMessageBox, QFileDialog, QCheckBox, QSpinBox,
0009                              QComboBox)
0010 import os
0011 import krita
0012 
0013 
0014 class UIExportLayers(object):
0015 
0016     def __init__(self):
0017         self.mainDialog = exportlayersdialog.ExportLayersDialog()
0018         self.mainLayout = QVBoxLayout(self.mainDialog)
0019         self.formLayout = QFormLayout()
0020         self.resSpinBoxLayout = QFormLayout()
0021         self.documentLayout = QVBoxLayout()
0022         self.directorySelectorLayout = QHBoxLayout()
0023         self.optionsLayout = QVBoxLayout()
0024         self.rectSizeLayout = QHBoxLayout()
0025 
0026         self.refreshButton = QPushButton(i18n("Refresh"))
0027         self.widgetDocuments = QListWidget()
0028         self.directoryTextField = QLineEdit()
0029         self.directoryDialogButton = QPushButton(i18n("..."))
0030         self.exportFilterLayersCheckBox = QCheckBox(
0031             i18n("Export filter layers"))
0032         self.batchmodeCheckBox = QCheckBox(i18n("Export in batchmode"))
0033         self.groupAsLayer = QCheckBox(i18n("Group as layer"))
0034         self.ignoreInvisibleLayersCheckBox = QCheckBox(
0035             i18n("Ignore invisible layers"))
0036         self.cropToImageBounds = QCheckBox(
0037                 i18n("Adjust export size to layer content"))
0038 
0039         self.rectWidthSpinBox = QSpinBox()
0040         self.rectHeightSpinBox = QSpinBox()
0041         self.formatsComboBox = QComboBox()
0042         self.resSpinBox = QSpinBox()
0043 
0044         self.buttonBox = QDialogButtonBox(
0045             QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
0046 
0047         self.kritaInstance = krita.Krita.instance()
0048         self.documentsList = []
0049 
0050         self.directoryTextField.setReadOnly(True)
0051         self.batchmodeCheckBox.setChecked(True)
0052         self.directoryDialogButton.clicked.connect(self._selectDir)
0053         self.widgetDocuments.currentRowChanged.connect(self._setResolution)
0054         self.refreshButton.clicked.connect(self.refreshButtonClicked)
0055         self.buttonBox.accepted.connect(self.confirmButton)
0056         self.buttonBox.rejected.connect(self.mainDialog.close)
0057         self.cropToImageBounds.stateChanged.connect(self._toggleCropSize)
0058 
0059         self.mainDialog.setWindowModality(Qt.NonModal)
0060         self.widgetDocuments.setSizeAdjustPolicy(
0061             QAbstractScrollArea.AdjustToContents)
0062 
0063     def initialize(self):
0064         self.loadDocuments()
0065 
0066         self.rectWidthSpinBox.setRange(1, 10000)
0067         self.rectHeightSpinBox.setRange(1, 10000)
0068         self.resSpinBox.setRange(20, 1200)
0069 
0070         self.formatsComboBox.addItem(i18n("JPEG"))
0071         self.formatsComboBox.addItem(i18n("PNG"))
0072 
0073         self.documentLayout.addWidget(self.widgetDocuments)
0074         self.documentLayout.addWidget(self.refreshButton)
0075 
0076         self.directorySelectorLayout.addWidget(self.directoryTextField)
0077         self.directorySelectorLayout.addWidget(self.directoryDialogButton)
0078 
0079         self.optionsLayout.addWidget(self.exportFilterLayersCheckBox)
0080         self.optionsLayout.addWidget(self.batchmodeCheckBox)
0081         self.optionsLayout.addWidget(self.groupAsLayer)
0082         self.optionsLayout.addWidget(self.ignoreInvisibleLayersCheckBox)
0083         self.optionsLayout.addWidget(self.cropToImageBounds)
0084 
0085         self.resSpinBoxLayout.addRow(i18n("dpi:"), self.resSpinBox)
0086 
0087         self.rectSizeLayout.addWidget(self.rectWidthSpinBox)
0088         self.rectSizeLayout.addWidget(self.rectHeightSpinBox)
0089         self.rectSizeLayout.addLayout(self.resSpinBoxLayout)
0090 
0091         self.formLayout.addRow(i18n("Documents:"), self.documentLayout)
0092         self.formLayout.addRow(
0093             i18n("Initial directory:"), self.directorySelectorLayout)
0094         self.formLayout.addRow(i18n("Export options:"), self.optionsLayout)
0095         self.formLayout.addRow(i18n("Export size:"), self.rectSizeLayout)
0096         self.formLayout.addRow(
0097             i18n("Images extensions:"), self.formatsComboBox)
0098 
0099         self.line = QFrame()
0100         self.line.setFrameShape(QFrame.HLine)
0101         self.line.setFrameShadow(QFrame.Sunken)
0102 
0103         self.mainLayout.addLayout(self.formLayout)
0104         self.mainLayout.addWidget(self.line)
0105         self.mainLayout.addWidget(self.buttonBox)
0106 
0107         self.mainDialog.resize(500, 300)
0108         self.mainDialog.setWindowTitle(i18n("Export Layers"))
0109         self.mainDialog.setSizeGripEnabled(True)
0110         self.mainDialog.show()
0111         self.mainDialog.activateWindow()
0112 
0113     def loadDocuments(self):
0114         self.widgetDocuments.clear()
0115 
0116         self.documentsList = [
0117             document for document in self.kritaInstance.documents()
0118             if document.fileName()
0119         ]
0120 
0121         for document in self.documentsList:
0122             self.widgetDocuments.addItem(document.fileName())
0123 
0124     def refreshButtonClicked(self):
0125         self.loadDocuments()
0126 
0127     def confirmButton(self):
0128         selectedPaths = [
0129             item.text() for item in self.widgetDocuments.selectedItems()]
0130         selectedDocuments = [
0131             document for document in self.documentsList
0132             for path in selectedPaths if path == document.fileName()
0133         ]
0134 
0135         self.msgBox = QMessageBox(self.mainDialog)
0136         if not selectedDocuments:
0137             self.msgBox.setText(i18n("Select one document."))
0138         elif not self.directoryTextField.text():
0139             self.msgBox.setText(i18n("Select the initial directory."))
0140         else:
0141             self.export(selectedDocuments[0])
0142             self.msgBox.setText(i18n("All layers have been exported."))
0143         self.msgBox.exec_()
0144 
0145     def mkdir(self, directory):
0146         target_directory = self.directoryTextField.text() + directory
0147         if (os.path.exists(target_directory)
0148                 and os.path.isdir(target_directory)):
0149             return
0150 
0151         try:
0152             os.makedirs(target_directory)
0153         except OSError as e:
0154             raise e
0155 
0156     def export(self, document):
0157         Application.setBatchmode(self.batchmodeCheckBox.isChecked())
0158 
0159         documentName = document.fileName() if document.fileName() else 'Untitled'  # noqa: E501
0160         fileName, extension = os.path.splitext(os.path.basename(documentName))
0161         self.mkdir('/' + fileName)
0162 
0163         self._exportLayers(
0164             document.rootNode(),
0165             self.formatsComboBox.currentText(),
0166             '/' + fileName)
0167         Application.setBatchmode(True)
0168 
0169     def _exportLayers(self, parentNode, fileFormat, parentDir):
0170         """ This method get all sub-nodes from the current node and export then in
0171             the defined format."""
0172 
0173         for node in parentNode.childNodes():
0174             newDir = ''
0175             if node.type() == 'grouplayer' and not self.groupAsLayer.isChecked():
0176                 newDir = os.path.join(parentDir, node.name())
0177                 self.mkdir(newDir)
0178             elif (not self.exportFilterLayersCheckBox.isChecked()
0179                   and 'filter' in node.type()):
0180                 continue
0181             elif (self.ignoreInvisibleLayersCheckBox.isChecked()
0182                   and not node.visible()):
0183                 continue
0184             else:
0185                 nodeName = node.name()
0186                 _fileFormat = self.formatsComboBox.currentText()
0187                 if '[jpeg]' in nodeName:
0188                     _fileFormat = 'jpeg'
0189                 elif '[png]' in nodeName:
0190                     _fileFormat = 'png'
0191 
0192                 if self.cropToImageBounds.isChecked():
0193                     bounds = QRect()
0194                 else:
0195                     bounds = QRect(0, 0, self.rectWidthSpinBox.value(), self.rectHeightSpinBox.value())
0196 
0197                 layerFileName = '{0}{1}/{2}.{3}'.format(
0198                     self.directoryTextField.text(),
0199                     parentDir, node.name(), _fileFormat)
0200                 node.save(layerFileName, self.resSpinBox.value() / 72.,
0201                           self.resSpinBox.value() / 72., krita.InfoObject(), bounds)
0202 
0203             if node.childNodes() and not self.groupAsLayer.isChecked():
0204                 self._exportLayers(node, fileFormat, newDir)
0205 
0206     def _selectDir(self):
0207         directory = QFileDialog.getExistingDirectory(
0208             self.mainDialog,
0209             i18n("Select a Folder"),
0210             os.path.expanduser("~"),
0211             QFileDialog.ShowDirsOnly)
0212         self.directoryTextField.setText(directory)
0213 
0214     def _setResolution(self, index):
0215         document = self.documentsList[index]
0216         self.rectWidthSpinBox.setValue(document.width())
0217         self.rectHeightSpinBox.setValue(document.height())
0218         self.resSpinBox.setValue(document.resolution())
0219 
0220     def _toggleCropSize(self):
0221         cropToLayer = self.cropToImageBounds.isChecked()
0222         self.rectWidthSpinBox.setDisabled(cropToLayer)
0223         self.rectHeightSpinBox.setDisabled(cropToLayer)