card_grid_generator_extension.py 7.6 KB

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