card_grid_generator_extension.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. from krita import Extension
  2. from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
  3. from PyQt5.QtCore import Qt, QByteArray
  4. from math import floor
  5. class CardGridGenerator(Extension):
  6. def __init__(self, parent):
  7. super().__init__(parent)
  8. # Krita.instance() exists, so do any setup work
  9. def setup(self):
  10. pass
  11. # called after setup(self)
  12. def createActions(self, window):
  13. # Create menu item in Tools > Scripts.
  14. action = window.createAction("cardgridgen", "Card Grid Generator")
  15. action.triggered.connect(self.card_grid_generator)
  16. def card_grid_generator(self):
  17. # Dialog creation.
  18. newDialog = QDialog()
  19. newDialog.setWindowTitle("Card Grid Generator")
  20. layout = QVBoxLayout()
  21. newDialog.setLayout(layout)
  22. # Description row.
  23. desc = QLabel('Creates a grid of guides and cropmarks for a given card size.')
  24. desc.setAlignment(Qt.AlignCenter)
  25. row0 = QHBoxLayout()
  26. row0.addWidget(desc)
  27. layout.addLayout(row0)
  28. # Card dimension row.
  29. self.widthInput = QLineEdit('750')
  30. self.heightInput = QLineEdit('1050')
  31. self.unitInput = QComboBox()
  32. self.unitInput.addItems( ['px', 'mm', 'inch'] )
  33. row1 = QHBoxLayout()
  34. row1.addWidget(QLabel('Card size: W:'))
  35. row1.addWidget(self.widthInput)
  36. row1.addWidget(QLabel(' x H:'))
  37. row1.addWidget(self.heightInput)
  38. row1.addWidget(self.unitInput)
  39. layout.addLayout(row1)
  40. # Bleed dimension row.
  41. self.bleedInput = QLineEdit('36')
  42. self.guidesCheck = QCheckBox('Guides')
  43. self.guidesCheck.setChecked(True)
  44. row2 = QHBoxLayout()
  45. row2.addWidget(QLabel('Bleed size:'))
  46. row2.addWidget(self.bleedInput)
  47. row2.addWidget(self.guidesCheck)
  48. layout.addLayout(row2)
  49. # How many cards row.
  50. self.rowsInput = QLineEdit('0')
  51. self.colsInput = QLineEdit('0')
  52. self.maxCheck = QCheckBox('Max Possible')
  53. self.maxCheck.setChecked(True)
  54. row3 = QHBoxLayout()
  55. row3.addWidget(QLabel('Grid: Columns:'))
  56. row3.addWidget(self.colsInput)
  57. row3.addWidget(QLabel('Rows:'))
  58. row3.addWidget(self.rowsInput)
  59. row3.addWidget(self.maxCheck)
  60. layout.addLayout(row3)
  61. # Cropmark options.
  62. self.cropmarksCheck = QCheckBox('Cropmarks')
  63. self.cropmarksCheck.setChecked(True)
  64. self.cropmarkTypeInput = QComboBox()
  65. self.cropmarkTypeInput.addItems( ['Around', 'Between', 'All'] )
  66. row4 = QHBoxLayout()
  67. row4.addWidget(self.cropmarksCheck)
  68. row4.addWidget(QLabel('Cropmark Type:'))
  69. row4.addWidget(self.cropmarkTypeInput)
  70. layout.addLayout(row4)
  71. # Do It row.
  72. goButton = QPushButton("Create Card Grid")
  73. goButton.setIcon( Krita.instance().icon('animation_play') )
  74. row5 = QHBoxLayout()
  75. row5.addWidget(goButton)
  76. layout.addLayout(row5)
  77. # Hook up the actions.
  78. goButton.clicked.connect( self.generateCardGrid )
  79. # Show the dialog.
  80. newDialog.exec_()
  81. ##########
  82. # Slots
  83. ##########
  84. # Actually generates the card grid!
  85. def generateCardGrid(self, e):
  86. doc = Krita.instance().activeDocument()
  87. # If there is no open document, create a new one (A4 300ppi).
  88. if not doc:
  89. doc = Krita.instance().createDocument(3508, 2480, "Card Grid", "RGBA", "U8", "", 300.0)
  90. Krita.instance().activeWindow().addView(doc)
  91. # Document dimensions.
  92. docWidth = doc.width()
  93. docHeight = doc.height()
  94. # Card dimensions.
  95. multiplier = 1
  96. docPPI = doc.resolution()
  97. if self.unitInput.currentText() == "inch":
  98. multiplier = docPPI
  99. if self.unitInput.currentText() == "mm":
  100. multiplier = docPPI/25.4
  101. bleedSize = float(self.bleedInput.text()) * multiplier
  102. cardWidth = float(self.widthInput.text()) * multiplier
  103. cardHeight = float(self.heightInput.text()) * multiplier
  104. # Card dimensions with bleed.
  105. cardBledWidth = cardWidth + bleedSize * 2
  106. cardBledHeight = cardHeight + bleedSize * 2
  107. # Determine layout.
  108. colCount = 0
  109. rowCount = 0
  110. if self.maxCheck.checkState():
  111. # Create the maximum-possible card layout
  112. colCount = floor(docWidth / cardBledWidth)
  113. rowCount = floor(docHeight / cardBledHeight)
  114. else:
  115. # Create card layout according to user specified rows and columns.
  116. colCount = int(self.colsInput.text())
  117. rowCount = int(self.rowsInput.text())
  118. # Calculate offset for centering.
  119. # Remainder pixels of row and column, divide by 2.
  120. # Bias odd pixel calculations to top left via floor.
  121. widthOffset = floor((docWidth - colCount * cardBledWidth) / 2)
  122. heightOffset = floor((docHeight - rowCount * cardBledHeight) / 2)
  123. # Create xSet and ySet (remember, both bleed + edge dimensions).
  124. # Also create Cards-Only sets for cropmarks.
  125. xSet = [widthOffset]
  126. ySet = [heightOffset]
  127. xSetCardsOnly = []
  128. ySetCardsOnly = []
  129. for i in range(colCount):
  130. base = widthOffset + i * cardBledWidth
  131. bleed1 = base + bleedSize
  132. card = bleed1 + cardWidth
  133. bleed2 = card + bleedSize
  134. # Cut down the number of guides if there is no bleed.
  135. xSet += [bleed1, card, bleed2] if bleedSize > 0 else [card]
  136. xSetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  137. for i in range(rowCount):
  138. base = heightOffset + i * cardBledHeight
  139. bleed1 = base + bleedSize
  140. card = bleed1 + cardHeight
  141. bleed2 = card + bleedSize
  142. # Cut down the number of guides if there is no bleed.
  143. ySet +=[bleed1, card, bleed2] if bleedSize > 0 else [card]
  144. ySetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  145. xSet.sort()
  146. ySet.sort()
  147. # Create guides. dump xSet ySet into guide functions.
  148. if self.guidesCheck.checkState():
  149. vList = doc.verticalGuides()
  150. hList = doc.horizontalGuides()
  151. vList.clear()
  152. hList.clear()
  153. doc.setVerticalGuides(xSet)
  154. doc.setHorizontalGuides(ySet)
  155. doc.setGuidesLocked(True)
  156. doc.setGuidesVisible(True)
  157. # Create cropmarks.
  158. if self.cropmarksCheck.checkState():
  159. cropmarkType = self.cropmarkTypeInput.currentText()
  160. cropAround = cropmarkType in ["Around", "All"]
  161. cropBetween = cropmarkType in ["Between", "All"]
  162. # Cropmarks will be on a new layer.
  163. layer = doc.createNode('CardGridCropmarks', 'paintLayer')
  164. root = doc.rootNode()
  165. root.addChildNode(layer, None)
  166. # Create vertical cropmarks.
  167. blackPixel = b'\x00\x00\x00\xff'
  168. cropMarkWidth = 2
  169. cropMarkHeight = 48
  170. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  171. for x in xSetCardsOnly:
  172. yToDo = []
  173. if cropAround:
  174. yToDo.append(ySet[0] - cropMarkHeight) # top
  175. yToDo.append(ySet[-1]) # bottom
  176. if cropBetween:
  177. for y in ySetCardsOnly:
  178. yToDo.append(y - cropMarkHeight/2)
  179. for y in yToDo:
  180. layer.setPixelData(cropMark, x - 1, y, cropMarkWidth, cropMarkHeight)
  181. # Create horizontal cropmarks.
  182. cropMarkWidth = 48
  183. cropMarkHeight = 2
  184. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  185. for y in ySetCardsOnly:
  186. xToDo = []
  187. if cropAround:
  188. xToDo.append(xSet[0] - cropMarkWidth) # left
  189. xToDo.append(xSet[-1]) # right
  190. if cropBetween:
  191. for x in xSetCardsOnly:
  192. xToDo.append(x - cropMarkWidth/2)
  193. for x in xToDo:
  194. layer.setPixelData(cropMark, x, y - 1, cropMarkWidth, cropMarkHeight)
  195. # Refresh the view, or the cropmarks will not be immediately shown.
  196. doc.refreshProjection()