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()