from krita import DockWidget from PyQt5.QtWidgets import QWidget, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton DOCKER_TITLE = 'Bleed Generator' class BleedGenerator(DockWidget): def __init__(self): super().__init__() self.setWindowTitle(DOCKER_TITLE) widget = QWidget() layout = QVBoxLayout() widget.setLayout(layout) layout.addSpacing(10) layout.addWidget(QLabel('Adds bleed to the active paint layer.')) # Count and unit inputs. Default to 24px (2mm at 300dpi). self.countInput = QLineEdit('24') self.unitInput = QComboBox() self.unitInput.addItems( ['px', 'mm', 'inch'] ) row1 = QHBoxLayout() row1.addWidget(QLabel('Bleed amount:')) row1.addWidget(self.countInput) row1.addWidget(self.unitInput) layout.addLayout(row1) # Option to bleed to boundaries. self.bleedToBoundsCheck = QCheckBox('Bleed to nearest guides/boundaries') self.bleedToBoundsCheck.setChecked(False) layout.addWidget(self.bleedToBoundsCheck) # Offset input i.e. how far from edge to start copying lines. self.offsetInput = QLineEdit('0') row2 = QHBoxLayout() row2.addWidget(QLabel('Offset (0 = use edge):')) row2.addWidget(self.offsetInput) row2.addWidget(QLabel('px')) layout.addLayout(row2) # Side selection self.topCheck = QCheckBox('Top') self.topCheck.setChecked(True) self.botCheck = QCheckBox('Bottom') self.botCheck.setChecked(True) self.leftCheck = QCheckBox('Left') self.leftCheck.setChecked(True) self.rightCheck = QCheckBox('Right') self.rightCheck.setChecked(True) layout.addWidget(QLabel('Sides to bleed:')) row3 = QHBoxLayout() row3.addWidget(self.topCheck) row3.addWidget(self.botCheck) row3.addWidget(self.leftCheck) row3.addWidget(self.rightCheck) layout.addLayout(row3) # Button! layout.addSpacing(20) goButton = QPushButton("Add Bleed") goButton.setIcon( Krita.instance().icon('animation_play') ) layout.addWidget(goButton) # Add a stretch to prevent the rest of the content from stretching. layout.addStretch() # Add widget to the docker. self.setWidget(widget) # Hook up the action to the button. goButton.clicked.connect( self.generateBleed ) # notifies when views are added or removed # 'pass' means do not do anything def canvasChanged(self, canvas): pass ########## # Slots ########## # Actually generates the bleed. def generateBleed(self, e): # Get the current layer self.doc = Krita.instance().activeDocument() self.layer = self.doc.activeNode() if self.layer.type() != 'paintlayer': dialog = QDialog() dialog.setWindowTitle("Paint Layer Required") layout = QVBoxLayout() layout.addWidget(QLabel('Bleed generator only works on paint layers. Please select one.')) dialog.setLayout(layout) dialog.exec_() return self.offset = round(float(self.offsetInput.text())) # Calculate how many lines of pixels to copy for each side amounts = self.calculateBleedAmounts() # Copy lines for selected sides. # Bounds are fetched each time as layer dimensions change. if self.topCheck.checkState(): bds = self.layer.bounds() xpos = bds.left() ypos = bds.top() - self.offset len = bds.width() bleedline = self.layer.pixelData(xpos, ypos, len, 1) for c in range(1, amounts['top']+1): self.layer.setPixelData(bleedline, xpos, (ypos - c), len, 1) if self.botCheck.checkState(): bds = self.layer.bounds() xpos = bds.left() ypos = bds.bottom() + self.offset len = bds.width() bleedline = self.layer.pixelData(xpos, ypos, len, 1) for c in range(1, amounts['bottom']+1): self.layer.setPixelData(bleedline, xpos, (ypos + c), len, 1) if self.leftCheck.checkState(): bds = self.layer.bounds() xpos = bds.left() - self.offset ypos = bds.top() len = bds.height() bleedline = self.layer.pixelData(xpos, ypos, 1, len) for c in range(1, amounts['left']+1): self.layer.setPixelData(bleedline, (xpos - c), ypos, 1, len) if self.rightCheck.checkState(): bds = self.layer.bounds() xpos = bds.right() + self.offset ypos = bds.top() len = bds.height() bleedline = self.layer.pixelData(xpos, ypos, 1, len) for c in range(1, amounts['right']+1): self.layer.setPixelData(bleedline, (xpos + c), ypos, 1, len) # Refresh the view, or the cloned pixels will not be immediately shown. self.doc.refreshProjection() def calculateBleedAmounts(self): amounts = {} if self.bleedToBoundsCheck.checkState(): # Bleed amounts determined from boundaries. # Get all boundaries. vPoints = self.doc.horizontalGuides() hPoints = self.doc.verticalGuides() vPoints.insert(0, 0) vPoints.append(self.doc.height()) vPoints.sort() hPoints.insert(0, 0) hPoints.append(self.doc.width()) hPoints.sort() bds = self.layer.bounds() # Calculate bleed from layer's edge to next relevant boundary. prevV = 0 top = bds.top() bottom = bds.bottom() + 1 for v in vPoints: v = round(v) if 'top' not in amounts and v >= top: amounts['top'] = top - prevV if 'bottom' not in amounts and v > bottom: amounts['bottom'] = v - bottom break prevV = v prevH = 0 left = bds.left() right = bds.right() + 1 for h in hPoints: h = round(h) if 'left' not in amounts and h >= left: amounts['left'] = left - prevH if 'right' not in amounts and h > right: amounts['right'] = h - right prevH = h else: # Bleed amounts based on the user's input. unit = self.unitInput.currentIndex() count = float(self.countInput.text()) ppi = self.doc.resolution() if unit == 1: # mm count = count * (ppi / 24.5) if unit == 2: # inch count = count * ppi count = round(count) - self.offset amounts['top'] = count amounts['bottom'] = count amounts['left'] = count amounts['right'] = count return amounts