card_grid_generator_extension.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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.bleedInput = QLineEdit('36')
  32. self.unitInput = QComboBox()
  33. self.unitInput.addItems( ['px', 'mm', 'inch'] )
  34. row1 = QHBoxLayout()
  35. row1.addWidget(QLabel('Card size - W:'))
  36. row1.addWidget(self.widthInput)
  37. row1.addWidget(QLabel(' x H:'))
  38. row1.addWidget(self.heightInput)
  39. row1.addWidget(QLabel(' + Bleed:'))
  40. row1.addWidget(self.bleedInput)
  41. row1.addWidget(self.unitInput)
  42. layout.addLayout(row1)
  43. # How many cards row.
  44. self.rowsInput = QLineEdit('0')
  45. self.colsInput = QLineEdit('0')
  46. self.maxCheck = QCheckBox('Max Possible')
  47. self.maxCheck.setChecked(True)
  48. row3 = QHBoxLayout()
  49. row3.addWidget(QLabel('Grid - Columns:'))
  50. row3.addWidget(self.colsInput)
  51. row3.addWidget(QLabel('Rows:'))
  52. row3.addWidget(self.rowsInput)
  53. row3.addWidget(self.maxCheck)
  54. layout.addLayout(row3)
  55. # Cropmark options.
  56. self.guidesCheck = QCheckBox('Guides')
  57. self.guidesCheck.setChecked(True)
  58. self.cropmarksCheck = QCheckBox('Cropmarks')
  59. self.cropmarksCheck.setChecked(True)
  60. self.cropmarkTypeInput = QComboBox()
  61. self.cropmarkTypeInput.addItems( ['Around', 'Between', 'All'] )
  62. row4 = QHBoxLayout()
  63. row4.addWidget(self.guidesCheck)
  64. row4.addWidget(self.cropmarksCheck)
  65. row4.addWidget(QLabel('Cropmark Type:'))
  66. row4.addWidget(self.cropmarkTypeInput)
  67. layout.addLayout(row4)
  68. # Do It row.
  69. goButton = QPushButton("Create Card Grid")
  70. goButton.setIcon( Krita.instance().icon('animation_play') )
  71. row5 = QHBoxLayout()
  72. row5.addWidget(goButton)
  73. layout.addLayout(row5)
  74. # Hook up the actions.
  75. goButton.clicked.connect( self.generateCardGrid )
  76. # Show the dialog.
  77. newDialog.exec_()
  78. ##########
  79. # Slots
  80. ##########
  81. # Actually generates the card grid!
  82. def generateCardGrid(self, e):
  83. doc = Krita.instance().activeDocument()
  84. # If there is no open document, create a new one (A4 300ppi).
  85. if not doc:
  86. doc = Krita.instance().createDocument(3508, 2480, "Card Grid", "RGBA", "U8", "", 300.0)
  87. Krita.instance().activeWindow().addView(doc)
  88. # Document dimensions.
  89. docWidth = doc.width()
  90. docHeight = doc.height()
  91. # Card dimensions.
  92. multiplier = 1
  93. docPPI = doc.resolution()
  94. if self.unitInput.currentText() == "inch":
  95. multiplier = docPPI
  96. if self.unitInput.currentText() == "mm":
  97. multiplier = docPPI/25.4
  98. bleed = self.bleedInput.text()
  99. width = self.widthInput.text()
  100. height = self.heightInput.text()
  101. bleedSize = int(float(bleed) * multiplier)
  102. cardWidth = int(float(width) * multiplier)
  103. cardHeight = int(float(height) * 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 left-most card starting coords, offset for centering.
  119. # Remainder pixels of row and column, divide by 2.
  120. # Bias odd pixel lengths to top left (via floor).
  121. widthOffset = floor((docWidth - colCount * cardBledWidth) / 2)
  122. heightOffset = floor((docHeight - rowCount * cardBledHeight) / 2)
  123. # Create xSet and ySet (arrays of x and y coords for guide creating,
  124. # with both bleed + edge dimensions).
  125. # Also create Cards-Only sets for "between" cropmarks.
  126. xSet = [widthOffset]
  127. ySet = [heightOffset]
  128. xSetCardsOnly = [widthOffset]
  129. ySetCardsOnly = [heightOffset]
  130. for i in range(colCount):
  131. base = widthOffset + i * cardBledWidth
  132. bleed1 = base + bleedSize
  133. card = bleed1 + cardWidth
  134. bleed2 = card + bleedSize
  135. # Cut down the number of guides if there is no bleed.
  136. xSet += [bleed1, card, bleed2] if bleedSize > 0 else [card]
  137. xSetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  138. for i in range(rowCount):
  139. base = heightOffset + i * cardBledHeight
  140. bleed1 = base + bleedSize
  141. card = bleed1 + cardHeight
  142. bleed2 = card + bleedSize
  143. # Cut down the number of guides if there is no bleed.
  144. ySet +=[bleed1, card, bleed2] if bleedSize > 0 else [card]
  145. ySetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
  146. xSet.sort()
  147. ySet.sort()
  148. # Create guides. dump xSet ySet into guide functions.
  149. if self.guidesCheck.checkState():
  150. vList = doc.verticalGuides()
  151. hList = doc.horizontalGuides()
  152. vList.clear()
  153. hList.clear()
  154. doc.setVerticalGuides(xSet)
  155. doc.setHorizontalGuides(ySet)
  156. doc.setGuidesLocked(True)
  157. doc.setGuidesVisible(True)
  158. # Create cropmarks.
  159. if self.cropmarksCheck.checkState():
  160. cropmarkType = self.cropmarkTypeInput.currentText()
  161. cropAround = cropmarkType in ["Around", "All"]
  162. cropBetween = cropmarkType in ["Between", "All"]
  163. # Cropmarks will be on a new layer.
  164. layer = doc.createNode(f"""Cropmarks {width}x{height}x{bleed}""", 'paintLayer')
  165. root = doc.rootNode()
  166. root.addChildNode(layer, None)
  167. blackPixel = b'\x00\x00\x00\xff'
  168. cropMarkWidth = 2
  169. # Create outer cropmarks.
  170. if cropAround:
  171. cropMarkHeight = 48
  172. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  173. for x in xSetCardsOnly:
  174. layer.setPixelData(cropMark, x-1, ySet[0]-cropMarkHeight, cropMarkWidth, cropMarkHeight) # top vertical line
  175. layer.setPixelData(cropMark, x-1, ySet[-1], cropMarkWidth, cropMarkHeight) # bottom vertical line
  176. for y in ySetCardsOnly:
  177. layer.setPixelData(cropMark, xSet[0]-cropMarkHeight, y-1, cropMarkHeight, cropMarkWidth) # left horizontal line
  178. layer.setPixelData(cropMark, xSet[-1], y-1, cropMarkHeight, cropMarkWidth) # right horizontal line
  179. # Create inner cropmarks.
  180. if cropBetween:
  181. cropMarkHeight = 36
  182. cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
  183. for x in xSetCardsOnly:
  184. for y in ySetCardsOnly:
  185. layer.setPixelData(cropMark, x-1, int(y - cropMarkHeight/2), cropMarkWidth, cropMarkHeight) # vertical line
  186. layer.setPixelData(cropMark, int(x - cropMarkHeight/2), y-1, cropMarkHeight, cropMarkWidth) # horizontal line
  187. # Refresh the view, or the cropmarks will not be immediately shown.
  188. doc.refreshProjection()