uiexportlayers.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # This script is licensed CC 0 1.0, so that you can learn from it.
  2. # ------ CC 0 1.0 ---------------
  3. # The person who associated a work with this deed has dedicated the
  4. # work to the public domain by waiving all of his or her rights to the
  5. # work worldwide under copyright law, including all related and
  6. # neighboring rights, to the extent allowed by law.
  7. # You can copy, modify, distribute and perform the work, even for
  8. # commercial purposes, all without asking permission.
  9. # https://creativecommons.org/publicdomain/zero/1.0/legalcode
  10. from . import exportlayersdialog
  11. from PyQt5.QtCore import (Qt, QRect)
  12. from PyQt5.QtWidgets import (QFormLayout, QListWidget, QHBoxLayout,
  13. QDialogButtonBox, QVBoxLayout, QFrame,
  14. QPushButton, QAbstractScrollArea, QLineEdit,
  15. QMessageBox, QFileDialog, QCheckBox, QSpinBox,
  16. QComboBox)
  17. import os
  18. import krita
  19. class UIExportLayers(object):
  20. def __init__(self):
  21. self.mainDialog = exportlayersdialog.ExportLayersDialog()
  22. self.mainLayout = QVBoxLayout(self.mainDialog)
  23. self.formLayout = QFormLayout()
  24. self.resSpinBoxLayout = QFormLayout()
  25. self.documentLayout = QVBoxLayout()
  26. self.directorySelectorLayout = QHBoxLayout()
  27. self.optionsLayout = QVBoxLayout()
  28. self.rectSizeLayout = QHBoxLayout()
  29. self.refreshButton = QPushButton(i18n("Refresh"))
  30. self.widgetDocuments = QListWidget()
  31. self.directoryTextField = QLineEdit()
  32. self.directoryDialogButton = QPushButton(i18n("..."))
  33. self.exportFilterLayersCheckBox = QCheckBox(
  34. i18n("Export filter layers"))
  35. self.batchmodeCheckBox = QCheckBox(i18n("Export in batchmode"))
  36. self.groupAsLayer = QCheckBox(i18n("Group as layer"))
  37. self.ignoreInvisibleLayersCheckBox = QCheckBox(
  38. i18n("Ignore invisible layers"))
  39. self.cropToImageBounds = QCheckBox(
  40. i18n("Adjust export size to layer content"))
  41. self.rectWidthSpinBox = QSpinBox()
  42. self.rectHeightSpinBox = QSpinBox()
  43. self.formatsComboBox = QComboBox()
  44. self.resSpinBox = QSpinBox()
  45. self.buttonBox = QDialogButtonBox(
  46. QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
  47. self.kritaInstance = krita.Krita.instance()
  48. self.documentsList = []
  49. self.directoryTextField.setReadOnly(True)
  50. self.batchmodeCheckBox.setChecked(True)
  51. self.directoryDialogButton.clicked.connect(self._selectDir)
  52. self.widgetDocuments.currentRowChanged.connect(self._setResolution)
  53. self.refreshButton.clicked.connect(self.refreshButtonClicked)
  54. self.buttonBox.accepted.connect(self.confirmButton)
  55. self.buttonBox.rejected.connect(self.mainDialog.close)
  56. self.cropToImageBounds.stateChanged.connect(self._toggleCropSize)
  57. self.mainDialog.setWindowModality(Qt.NonModal)
  58. self.widgetDocuments.setSizeAdjustPolicy(
  59. QAbstractScrollArea.AdjustToContents)
  60. def initialize(self):
  61. self.loadDocuments()
  62. self.rectWidthSpinBox.setRange(1, 10000)
  63. self.rectHeightSpinBox.setRange(1, 10000)
  64. self.resSpinBox.setRange(20, 1200)
  65. self.formatsComboBox.addItem(i18n("PNG"))
  66. self.formatsComboBox.addItem(i18n("JPEG"))
  67. self.documentLayout.addWidget(self.widgetDocuments)
  68. self.documentLayout.addWidget(self.refreshButton)
  69. self.directorySelectorLayout.addWidget(self.directoryTextField)
  70. self.directorySelectorLayout.addWidget(self.directoryDialogButton)
  71. self.optionsLayout.addWidget(self.exportFilterLayersCheckBox)
  72. self.optionsLayout.addWidget(self.batchmodeCheckBox)
  73. self.optionsLayout.addWidget(self.groupAsLayer)
  74. self.optionsLayout.addWidget(self.ignoreInvisibleLayersCheckBox)
  75. self.optionsLayout.addWidget(self.cropToImageBounds)
  76. self.resSpinBoxLayout.addRow(i18n("dpi:"), self.resSpinBox)
  77. self.rectSizeLayout.addWidget(self.rectWidthSpinBox)
  78. self.rectSizeLayout.addWidget(self.rectHeightSpinBox)
  79. self.rectSizeLayout.addLayout(self.resSpinBoxLayout)
  80. self.formLayout.addRow(i18n("Documents:"), self.documentLayout)
  81. self.formLayout.addRow(
  82. i18n("Initial directory:"), self.directorySelectorLayout)
  83. self.formLayout.addRow(i18n("Export options:"), self.optionsLayout)
  84. self.formLayout.addRow(i18n("Export size:"), self.rectSizeLayout)
  85. self.formLayout.addRow(
  86. i18n("Images extensions:"), self.formatsComboBox)
  87. self.line = QFrame()
  88. self.line.setFrameShape(QFrame.HLine)
  89. self.line.setFrameShadow(QFrame.Sunken)
  90. self.mainLayout.addLayout(self.formLayout)
  91. self.mainLayout.addWidget(self.line)
  92. self.mainLayout.addWidget(self.buttonBox)
  93. self.mainDialog.resize(500, 300)
  94. self.mainDialog.setWindowTitle(i18n("Export Layers"))
  95. self.mainDialog.setSizeGripEnabled(True)
  96. self.mainDialog.show()
  97. self.mainDialog.activateWindow()
  98. def loadDocuments(self):
  99. self.widgetDocuments.clear()
  100. self.documentsList = [
  101. document for document in self.kritaInstance.documents()
  102. if document.fileName()
  103. ]
  104. for document in self.documentsList:
  105. self.widgetDocuments.addItem(document.fileName())
  106. def refreshButtonClicked(self):
  107. self.loadDocuments()
  108. def confirmButton(self):
  109. selectedPaths = [
  110. item.text() for item in self.widgetDocuments.selectedItems()]
  111. selectedDocuments = [
  112. document for document in self.documentsList
  113. for path in selectedPaths if path == document.fileName()
  114. ]
  115. self.msgBox = QMessageBox(self.mainDialog)
  116. if not selectedDocuments:
  117. self.msgBox.setText(i18n("Select one document."))
  118. elif not self.directoryTextField.text():
  119. self.msgBox.setText(i18n("Select the initial directory."))
  120. else:
  121. self.export(selectedDocuments[0])
  122. self.msgBox.setText(i18n("All layers has been exported."))
  123. self.msgBox.exec_()
  124. def mkdir(self, directory):
  125. target_directory = self.directoryTextField.text() + directory
  126. if (os.path.exists(target_directory)
  127. and os.path.isdir(target_directory)):
  128. return
  129. try:
  130. os.makedirs(target_directory)
  131. except OSError as e:
  132. raise e
  133. def export(self, document):
  134. Application.setBatchmode(self.batchmodeCheckBox.isChecked())
  135. documentName = document.fileName() if document.fileName() else 'Untitled' # noqa: E501
  136. fileName, extension = os.path.splitext(os.path.basename(documentName))
  137. self.mkdir('/' + fileName)
  138. self._exportLayers(
  139. document.rootNode(),
  140. self.formatsComboBox.currentText(),
  141. '/' + fileName)
  142. Application.setBatchmode(True)
  143. def _exportLayers(self, parentNode, fileFormat, parentDir):
  144. """ This method get all sub-nodes from the current node and export then in
  145. the defined format."""
  146. for node in parentNode.childNodes():
  147. newDir = ''
  148. if node.type() == 'grouplayer' and not self.groupAsLayer.isChecked():
  149. newDir = os.path.join(parentDir, node.name())
  150. self.mkdir(newDir)
  151. elif (not self.exportFilterLayersCheckBox.isChecked()
  152. and 'filter' in node.type()):
  153. continue
  154. elif (self.ignoreInvisibleLayersCheckBox.isChecked()
  155. and not node.visible()):
  156. continue
  157. else:
  158. nodeName = node.name()
  159. _fileFormat = self.formatsComboBox.currentText()
  160. exportConfig = krita.InfoObject()
  161. if '[jpeg]' in nodeName:
  162. _fileFormat = 'jpeg'
  163. elif '[png]' in nodeName:
  164. _fileFormat = 'png'
  165. if _fileFormat.lower() == 'png':
  166. # Values from https://api.kde.org/appscomplete-api/krita-apidocs/libs/libkis/html/classDocument.html#abda324b9beee9384879af8b7e7807cfa
  167. # https://api.kde.org/appscomplete-api/krita-apidocs/libs/libkis/html/classInfoObject.html#a45fd7051d88d4d02221cb81d54189bc3
  168. exportConfig.setProperty('alpha', False)
  169. exportConfig.setProperty('compression', 9)
  170. # exportConfig.setProperty('transparencyFillcolor', [255, 255, 255])
  171. if self.cropToImageBounds.isChecked():
  172. bounds = QRect()
  173. else:
  174. bounds = QRect(0, 0, self.rectWidthSpinBox.value(), self.rectHeightSpinBox.value())
  175. layerFileName = '{0}{1}/{2}.{3}'.format(
  176. self.directoryTextField.text(),
  177. parentDir, node.name(), _fileFormat)
  178. node.save(layerFileName, self.resSpinBox.value() / 72.,
  179. self.resSpinBox.value() / 72., exportConfig, bounds)
  180. if node.childNodes() and not self.groupAsLayer.isChecked():
  181. self._exportLayers(node, fileFormat, newDir)
  182. def _selectDir(self):
  183. directory = QFileDialog.getExistingDirectory(
  184. self.mainDialog,
  185. i18n("Select a Folder"),
  186. os.path.expanduser("~"),
  187. QFileDialog.ShowDirsOnly)
  188. self.directoryTextField.setText(directory)
  189. def _setResolution(self, index):
  190. document = self.documentsList[index]
  191. self.rectWidthSpinBox.setValue(document.width())
  192. self.rectHeightSpinBox.setValue(document.height())
  193. self.resSpinBox.setValue(document.resolution())
  194. def _toggleCropSize(self):
  195. cropToLayer = self.cropToImageBounds.isChecked()
  196. self.rectWidthSpinBox.setDisabled(cropToLayer)
  197. self.rectHeightSpinBox.setDisabled(cropToLayer)