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. self.bleedInput = QLineEdit('36') self.guidesCheck = QCheckBox('Guides') self.guidesCheck.setChecked(True) row2 = QHBoxLayout() row2.addWidget(QLabel('Bleed size:')) row2.addWidget(self.bleedInput) row2.addWidget(self.guidesCheck) 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) # Cropmark options. self.cropmarksCheck = QCheckBox('Cropmarks') self.cropmarksCheck.setChecked(True) self.cropmarkTypeInput = QComboBox() self.cropmarkTypeInput.addItems( ['Around', 'Between', 'All'] ) row4 = QHBoxLayout() row4.addWidget(self.cropmarksCheck) row4.addWidget(QLabel('Cropmark Type:')) row4.addWidget(self.cropmarkTypeInput) layout.addLayout(row4) # Do It row. goButton = QPushButton("Create Card Grid") goButton.setIcon( Krita.instance().icon('animation_play') ) row5 = QHBoxLayout() row5.addWidget(goButton) layout.addLayout(row5) # 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. multiplier = 1 docPPI = doc.resolution() if self.unitInput.currentText() == "inch": multiplier = docPPI if self.unitInput.currentText() == "mm": multiplier = docPPI/25.4 bleed = self.bleedInput.text() width = self.widthInput.text() height = self.heightInput.text() bleedSize = int(float(bleed) * multiplier) cardWidth = int(float(width) * multiplier) cardHeight = int(float(height) * multiplier) # Card dimensions with bleed. 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(): cropmarkType = self.cropmarkTypeInput.currentText() cropAround = cropmarkType in ["Around", "All"] cropBetween = cropmarkType in ["Between", "All"] # Cropmarks will be on a new layer. layer = doc.createNode(f"""Cropmarks {width}x{height}x{bleed}""", 'paintLayer') root = doc.rootNode() root.addChildNode(layer, None) blackPixel = b'\x00\x00\x00\xff' cropMarkWidth = 2 # Create outer cropmarks. if cropAround: cropMarkHeight = 48 cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight) for x in xSetCardsOnly: layer.setPixelData(cropMark, x-1, ySet[0]-cropMarkHeight, cropMarkWidth, cropMarkHeight) # top vertical line layer.setPixelData(cropMark, x-1, ySet[-1], cropMarkWidth, cropMarkHeight) # bottom vertical line for y in ySetCardsOnly: layer.setPixelData(cropMark, xSet[0]-cropMarkHeight, y-1, cropMarkHeight, cropMarkWidth) # left horizontal line layer.setPixelData(cropMark, xSet[-1], y-1, cropMarkHeight, cropMarkWidth) # right horizontal line # Create inner cropmarks. if cropBetween: cropMarkHeight = 36 cropMark = QByteArray(blackPixel * cropMarkWidth * cropMarkHeight) for x in xSetCardsOnly: for y in ySetCardsOnly: layer.setPixelData(cropMark, x-1, int(y - cropMarkHeight/2), cropMarkWidth, cropMarkHeight) # vertical line layer.setPixelData(cropMark, int(x - cropMarkHeight/2), y-1, cropMarkHeight, cropMarkWidth) # horizontal line # Refresh the view, or the cropmarks will not be immediately shown. doc.refreshProjection()