|
|
@@ -0,0 +1,204 @@
|
|
|
+from krita import Extension
|
|
|
+from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
|
|
|
+from PyQt5.QtCore import Qt, QByteArray
|
|
|
+from math import floor
|
|
|
+
|
|
|
+class CardGridGenerator(Extension):
|
|
|
+
|
|
|
+ def __init__(self, parent):
|
|
|
+ super().__init__(parent)
|
|
|
+
|
|
|
+ # Krita.instance() exists, so do any setup work
|
|
|
+ def setup(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ # called after setup(self)
|
|
|
+ def createActions(self, window):
|
|
|
+ # Create menu item in Tools > Scripts.
|
|
|
+ action = window.createAction("cardgridgen", "Card Grid Generator")
|
|
|
+ action.triggered.connect(self.card_grid_generator)
|
|
|
+
|
|
|
+ def card_grid_generator(self):
|
|
|
+ # Dialog creation.
|
|
|
+ newDialog = QDialog()
|
|
|
+ newDialog.setWindowTitle("Card Grid Generator")
|
|
|
+ layout = QVBoxLayout()
|
|
|
+ newDialog.setLayout(layout)
|
|
|
+
|
|
|
+
|
|
|
+ # Description row.
|
|
|
+ desc = QLabel('Creates a grid of guides and cropmarks for a given card size.')
|
|
|
+ desc.setAlignment(Qt.AlignCenter)
|
|
|
+ row0 = QHBoxLayout()
|
|
|
+ row0.addWidget(desc)
|
|
|
+ layout.addLayout(row0)
|
|
|
+
|
|
|
+ # Card dimension row.
|
|
|
+ self.widthInput = QLineEdit('750')
|
|
|
+ self.heightInput = QLineEdit('1050')
|
|
|
+ self.unitInput = QComboBox()
|
|
|
+ self.unitInput.addItems( ['px', 'mm', 'inch'] )
|
|
|
+ row1 = QHBoxLayout()
|
|
|
+ row1.addWidget(QLabel('Card size: W:'))
|
|
|
+ row1.addWidget(self.widthInput)
|
|
|
+ row1.addWidget(QLabel(' x H:'))
|
|
|
+ row1.addWidget(self.heightInput)
|
|
|
+ row1.addWidget(self.unitInput)
|
|
|
+ layout.addLayout(row1)
|
|
|
+
|
|
|
+ # Bleed dimension row. Also, guides or cropmarks selection.
|
|
|
+ self.bleedInput = QLineEdit('24')
|
|
|
+ self.guidesCheck = QCheckBox('Guides')
|
|
|
+ self.guidesCheck.setChecked(True)
|
|
|
+ self.cropmarksCheck = QCheckBox('Cropmarks')
|
|
|
+ self.cropmarksCheck.setChecked(True)
|
|
|
+ row2 = QHBoxLayout()
|
|
|
+ row2.addWidget(QLabel('Bleed size:'))
|
|
|
+ row2.addWidget(self.bleedInput)
|
|
|
+ row2.addWidget(self.guidesCheck)
|
|
|
+ row2.addWidget(self.cropmarksCheck)
|
|
|
+ layout.addLayout(row2)
|
|
|
+
|
|
|
+ # How many cards row.
|
|
|
+ self.rowsInput = QLineEdit('0')
|
|
|
+ self.colsInput = QLineEdit('0')
|
|
|
+ self.maxCheck = QCheckBox('Max Possible')
|
|
|
+ self.maxCheck.setChecked(True)
|
|
|
+ row3 = QHBoxLayout()
|
|
|
+ row3.addWidget(QLabel('Grid: Columns:'))
|
|
|
+ row3.addWidget(self.colsInput)
|
|
|
+ row3.addWidget(QLabel('Rows:'))
|
|
|
+ row3.addWidget(self.rowsInput)
|
|
|
+ row3.addWidget(self.maxCheck)
|
|
|
+ layout.addLayout(row3)
|
|
|
+
|
|
|
+ # Do It row.
|
|
|
+ goButton = QPushButton("Create Card Grid")
|
|
|
+ goButton.setIcon( Krita.instance().icon('animation_play') )
|
|
|
+ row4 = QHBoxLayout()
|
|
|
+ row4.addWidget(goButton)
|
|
|
+ layout.addLayout(row4)
|
|
|
+
|
|
|
+ # Hook up the actions.
|
|
|
+ goButton.clicked.connect( self.generateCardGrid )
|
|
|
+
|
|
|
+ # Show the dialog.
|
|
|
+ newDialog.exec_()
|
|
|
+
|
|
|
+
|
|
|
+ ##########
|
|
|
+ # Slots
|
|
|
+ ##########
|
|
|
+
|
|
|
+ # Actually generates the card grid!
|
|
|
+ def generateCardGrid(self, e):
|
|
|
+
|
|
|
+ doc = Krita.instance().activeDocument()
|
|
|
+
|
|
|
+ # If there is no open document, create a new one (A4 300ppi).
|
|
|
+ if not doc:
|
|
|
+ doc = Krita.instance().createDocument(3508, 2480, "Card Grid", "RGBA", "U8", "", 300.0)
|
|
|
+ Krita.instance().activeWindow().addView(doc)
|
|
|
+
|
|
|
+ # Document dimensions.
|
|
|
+ docWidth = doc.width()
|
|
|
+ docHeight = doc.height()
|
|
|
+
|
|
|
+ # Card dimensions with bleed.
|
|
|
+ bleedSize = int(self.bleedInput.text())
|
|
|
+ cardWidth = int(self.widthInput.text())
|
|
|
+ cardHeight = int(self.heightInput.text())
|
|
|
+ cardBledWidth = cardWidth + bleedSize * 2
|
|
|
+ cardBledHeight = cardHeight + bleedSize * 2
|
|
|
+
|
|
|
+ # Determine layout.
|
|
|
+ colCount = 0
|
|
|
+ rowCount = 0
|
|
|
+ if self.maxCheck.checkState():
|
|
|
+ # Create the maximum-possible card layout
|
|
|
+ colCount = floor(docWidth / cardBledWidth)
|
|
|
+ rowCount = floor(docHeight / cardBledHeight)
|
|
|
+ else:
|
|
|
+ # Create card layout according to user specified rows and columns.
|
|
|
+ colCount = int(self.colsInput.text())
|
|
|
+ rowCount = int(self.rowsInput.text())
|
|
|
+
|
|
|
+ # Calculate offset for centering.
|
|
|
+ # Remainder pixels of row and column, divide by 2.
|
|
|
+ # Bias odd pixel calculations to top left via floor.
|
|
|
+ widthOffset = floor((docWidth - colCount * cardBledWidth) / 2)
|
|
|
+ heightOffset = floor((docHeight - rowCount * cardBledHeight) / 2)
|
|
|
+
|
|
|
+ # Create xSet and ySet (remember, both bleed + edge dimensions).
|
|
|
+ # Also create Cards-Only sets for cropmarks.
|
|
|
+ xSet = [widthOffset]
|
|
|
+ ySet = [heightOffset]
|
|
|
+ xSetCardsOnly = []
|
|
|
+ ySetCardsOnly = []
|
|
|
+
|
|
|
+ for i in range(colCount):
|
|
|
+ base = widthOffset + i * cardBledWidth
|
|
|
+ bleed1 = base + bleedSize
|
|
|
+ card = bleed1 + cardWidth
|
|
|
+ bleed2 = card + bleedSize
|
|
|
+ # Cut down the number of guides if there is no bleed.
|
|
|
+ xSet += [bleed1, card, bleed2] if bleedSize > 0 else [card]
|
|
|
+ xSetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
|
|
|
+
|
|
|
+ for i in range(rowCount):
|
|
|
+ base = heightOffset + i * cardBledHeight
|
|
|
+ bleed1 = base + bleedSize
|
|
|
+ card = bleed1 + cardHeight
|
|
|
+ bleed2 = card + bleedSize
|
|
|
+ # Cut down the number of guides if there is no bleed.
|
|
|
+ ySet +=[bleed1, card, bleed2] if bleedSize > 0 else [card]
|
|
|
+ ySetCardsOnly += [bleed1, card] if bleedSize > 0 else [card]
|
|
|
+
|
|
|
+ xSet.sort()
|
|
|
+ ySet.sort()
|
|
|
+
|
|
|
+ # Create guides. dump xSet ySet into guide functions.
|
|
|
+ if self.guidesCheck.checkState():
|
|
|
+ vList = doc.verticalGuides()
|
|
|
+ hList = doc.horizontalGuides()
|
|
|
+ vList.clear()
|
|
|
+ hList.clear()
|
|
|
+ doc.setVerticalGuides(xSet)
|
|
|
+ doc.setHorizontalGuides(ySet)
|
|
|
+ doc.setGuidesLocked(True)
|
|
|
+ doc.setGuidesVisible(True)
|
|
|
+
|
|
|
+ # Create cropmarks.
|
|
|
+ if self.cropmarksCheck.checkState():
|
|
|
+
|
|
|
+ # Cropmarks will be on a new layer.
|
|
|
+ layer = doc.createNode('CardGridCropmarks', 'paintLayer')
|
|
|
+ root = doc.rootNode()
|
|
|
+ root.addChildNode(layer, None)
|
|
|
+
|
|
|
+ # Create vertical cropmarks.
|
|
|
+ blackPixel = b'\x00\x00\x00\xff'
|
|
|
+ cropMarkWidth = 2
|
|
|
+ cropMarkHeight = 48
|
|
|
+ cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
|
|
|
+ for x in xSetCardsOnly:
|
|
|
+ # top
|
|
|
+ y = ySet[0] - cropMarkHeight
|
|
|
+ layer.setPixelData(cropMark, x - 1, y, cropMarkWidth, cropMarkHeight)
|
|
|
+ # bottom
|
|
|
+ layer.setPixelData(cropMark, x - 1, ySet[-1], cropMarkWidth, cropMarkHeight)
|
|
|
+
|
|
|
+ # Create horizontal cropmarks.
|
|
|
+ cropMarkWidth = 48
|
|
|
+ cropMarkHeight = 2
|
|
|
+ cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight)
|
|
|
+ for y in ySetCardsOnly:
|
|
|
+ # left
|
|
|
+ x = xSet[0] - cropMarkWidth
|
|
|
+ layer.setPixelData(cropMark, x, y - 1, cropMarkWidth, cropMarkHeight)
|
|
|
+ # right
|
|
|
+ layer.setPixelData(cropMark, xSet[-1], y - 1, cropMarkWidth, cropMarkHeight)
|
|
|
+
|
|
|
+ # Refresh the view, or the cropmarks will not be immediately shown.
|
|
|
+ doc.refreshProjection()
|
|
|
+
|