card_grid_generator_extension.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. bleed = self.bleedInput.text()
  102. width = self.widthInput.text()
  103. height = self.heightInput.text()
  104. bleedSize = int(float(bleed) * multiplier)
  105. cardWidth = int(float(width) * multiplier)
  106. cardHeight = int(float(height) * multiplier)
  107. # Card dimensions with bleed.
  108. cardBledWidth = cardWidth + bleedSize * 2
  109. cardBledHeight = cardHeight + bleedSize * 2
  110. # Determine layout.
  111. colCount = 0
  112. rowCount = 0
  113. if self.maxCheck.checkState():
  114. # Create the maximum-possible card layout
  115. colCount = floor(docWidth / cardBledWidth)
  116. rowCount = floor(docHeight / cardBledHeight)
  117. else:
  118. # Create card layout according to user specified rows and columns.
  119. colCount = int(self.colsInput.text())
  120. rowCount = int(self.rowsInput.text())
  121. # Calculate offset for centering.
  122. # Remainder pixels of row and column, divide by 2.
  123. # Bias odd pixel calculations to top left via floor.
  124. widthOffset = floor((docWidth - colCount * cardBledWidth) / 2)
  125. heightOffset = floor((docHeight - rowCount * cardBledHeight) / 2)
  126. # Create xSet and ySet (remember, both bleed + edge dimensions).
  127. # Also create Cards-Only sets for cropmarks.
  128. xSet = [widthOffset]
  129. ySet = [heightOffset]
  130. xSetCardsOnly = []
  131. ySetCardsOnly = []
  132. for i in range(colCount):
  133. base = widthOffset + i * cardBledWidth
  134. bleed1 = base + bleedSize
  135. card = bleed1 + cardWidth
  136. bleed2 = card + bleedSize
  137. # Cut down the number of guides if there is no bleed.
  138. xSet += [bleed1, card, bleed2] if bleedSize > 0 else [card]
  139. xSetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  140. for i in range(rowCount):
  141. base = heightOffset + i * cardBledHeight
  142. bleed1 = base + bleedSize
  143. card = bleed1 + cardHeight
  144. bleed2 = card + bleedSize
  145. # Cut down the number of guides if there is no bleed.
  146. ySet +=[bleed1, card, bleed2] if bleedSize > 0 else [card]
  147. ySetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  148. xSet.sort()
  149. ySet.sort()
  150. # Create guides. dump xSet ySet into guide functions.
  151. if self.guidesCheck.checkState():
  152. vList = doc.verticalGuides()
  153. hList = doc.horizontalGuides()
  154. vList.clear()
  155. hList.clear()
  156. doc.setVerticalGuides(xSet)
  157. doc.setHorizontalGuides(ySet)
  158. doc.setGuidesLocked(True)
  159. doc.setGuidesVisible(True)
  160. # Create cropmarks.
  161. if self.cropmarksCheck.checkState():
  162. cropmarkType = self.cropmarkTypeInput.currentText()
  163. cropAround = cropmarkType in ["Around", "All"]
  164. cropBetween = cropmarkType in ["Between", "All"]
  165. # Cropmarks will be on a new layer.
  166. layer = doc.createNode(f"""Cropmarks {width}x{height}x{bleed}""", 'paintLayer')
  167. root = doc.rootNode()
  168. root.addChildNode(layer, None)
  169. blackPixel = b'\x00\x00\x00\xff'
  170. cropMarkWidth = 2
  171. # Create outer cropmarks.
  172. if cropAround:
  173. cropMarkHeight = 48
  174. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  175. for x in xSetCardsOnly:
  176. layer.setPixelData(cropMark, x-1, ySet[0]-cropMarkHeight, cropMarkWidth, cropMarkHeight) # top vertical line
  177. layer.setPixelData(cropMark, x-1, ySet[-1], cropMarkWidth, cropMarkHeight) # bottom vertical line
  178. for y in ySetCardsOnly:
  179. layer.setPixelData(cropMark, xSet[0]-cropMarkHeight, y-1, cropMarkHeight, cropMarkWidth) # left horizontal line
  180. layer.setPixelData(cropMark, xSet[-1], y-1, cropMarkHeight, cropMarkWidth) # right horizontal line
  181. # Create inner cropmarks.
  182. if cropBetween:
  183. cropMarkHeight = 36
  184. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  185. for x in xSetCardsOnly:
  186. for y in ySetCardsOnly:
  187. layer.setPixelData(cropMark, x-1, int(y - cropMarkHeight/2), cropMarkWidth, cropMarkHeight) # vertical line
  188. layer.setPixelData(cropMark, int(x - cropMarkHeight/2), y-1, cropMarkHeight, cropMarkWidth) # horizontal line
  189. # Refresh the view, or the cropmarks will not be immediately shown.
  190. doc.refreshProjection()