|
|
@@ -0,0 +1,146 @@
|
|
|
+from krita import DockWidget
|
|
|
+from PyQt5.QtWidgets import QWidget, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
|
|
|
+
|
|
|
+DOCKER_TITLE = 'Page Slicer'
|
|
|
+
|
|
|
+class PageSlicer(DockWidget):
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.setWindowTitle(DOCKER_TITLE)
|
|
|
+ widget = QWidget()
|
|
|
+ layout = QVBoxLayout()
|
|
|
+ widget.setLayout(layout)
|
|
|
+
|
|
|
+ layout.addSpacing(10)
|
|
|
+ layout.addWidget(QLabel('Slice up an image into segments using page guides.'))
|
|
|
+
|
|
|
+ # Thresholds for ignoring segments. Defaults to 100px in both dimensions.
|
|
|
+ # limitModes = ['<', '>']
|
|
|
+ self.widthInput = QLineEdit('100')
|
|
|
+ # self.widthLimitMode = QComboBox()
|
|
|
+ # self.widthLimitMode.addItems(limitModes)
|
|
|
+ self.heightInput = QLineEdit('100')
|
|
|
+ # self.heightLimitMode = QComboBox()
|
|
|
+ # self.heightLimitMode.addItems(limitModes)
|
|
|
+ layout.addWidget(QLabel('Segment-Ignore Thresholds:'))
|
|
|
+ row1 = QHBoxLayout()
|
|
|
+ row1.addWidget(QLabel('Width <'))
|
|
|
+ # row1.addWidget(self.widthLimitMode)
|
|
|
+ row1.addWidget(self.widthInput)
|
|
|
+ row1.addWidget(QLabel('px'))
|
|
|
+ layout.addLayout(row1)
|
|
|
+ row2 = QHBoxLayout()
|
|
|
+ row2.addWidget(QLabel('Height <'))
|
|
|
+ # row2.addWidget(self.heightLimitMode)
|
|
|
+ row2.addWidget(self.heightInput)
|
|
|
+ row2.addWidget(QLabel('px'))
|
|
|
+ layout.addLayout(row2)
|
|
|
+
|
|
|
+ # Use image bounds or not
|
|
|
+ self.useImageBoundsCheck = QCheckBox('Use Image Bounds')
|
|
|
+ self.useImageBoundsCheck.setChecked(False)
|
|
|
+ layout.addWidget(self.useImageBoundsCheck)
|
|
|
+
|
|
|
+ # Button!
|
|
|
+ layout.addSpacing(20)
|
|
|
+ goButton = QPushButton("Slice")
|
|
|
+ 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.slicePage )
|
|
|
+
|
|
|
+
|
|
|
+ # notifies when views are added or removed
|
|
|
+ # 'pass' means do not do anything
|
|
|
+ def canvasChanged(self, canvas):
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+ ##########
|
|
|
+ # Slots
|
|
|
+ ##########
|
|
|
+
|
|
|
+ # Actually slices the page.
|
|
|
+ def slicePage(self, e):
|
|
|
+
|
|
|
+ # Get the current layer.
|
|
|
+ doc = Krita.instance().activeDocument()
|
|
|
+ root = doc.rootNode()
|
|
|
+ layer = doc.activeNode()
|
|
|
+ if layer.type() != 'paintlayer':
|
|
|
+ dialog = QDialog()
|
|
|
+ dialog.setWindowTitle("Paint Layer Required")
|
|
|
+ layout = QVBoxLayout()
|
|
|
+ layout.addWidget(QLabel('Page slicer only works on paint layers. Please select one.'))
|
|
|
+ dialog.setLayout(layout)
|
|
|
+ dialog.exec_()
|
|
|
+ return
|
|
|
+
|
|
|
+ # Get horizontal and vertical points.
|
|
|
+ # Horizontal guides provide the Vertical y-axis points and vice versa.
|
|
|
+ vPoints = doc.horizontalGuides()
|
|
|
+ hPoints = doc.verticalGuides()
|
|
|
+
|
|
|
+ # Add the edges of the image for segment calculation, if needed.
|
|
|
+ useImageBounds = self.useImageBoundsCheck.checkState()
|
|
|
+ if useImageBounds:
|
|
|
+ vPoints.insert(0, 0)
|
|
|
+ vPoints.append(doc.height())
|
|
|
+ hPoints.insert(0, 0)
|
|
|
+ hPoints.append(doc.width())
|
|
|
+
|
|
|
+ # Sanitize points. Guide values are floats, and may contain unexpected
|
|
|
+ # extra fractional values.
|
|
|
+ vPoints = [round(v) for v in vPoints]
|
|
|
+ vPoints.sort()
|
|
|
+ hPoints = [round(h) for h in hPoints]
|
|
|
+ hPoints.sort()
|
|
|
+
|
|
|
+ # Check that there are enough vertical and horizontal points.
|
|
|
+ # When using image bounds there must be at least 1 guide (V or H).
|
|
|
+ # When not using image bounds, there must be at least 4 guides (2V, 2H).
|
|
|
+ imageBoundsLimitFail = True if len(vPoints) + len(hPoints) < 5 else False
|
|
|
+ nonImageBoundsLimitFail = True if len(vPoints) < 2 or len(hPoints) < 2 else False
|
|
|
+ if (useImageBounds and imageBoundsLimitFail) or nonImageBoundsLimitFail:
|
|
|
+ dialog = QDialog()
|
|
|
+ dialog.setWindowTitle("Insufficient Guides!")
|
|
|
+ layout = QVBoxLayout()
|
|
|
+ if useImageBounds:
|
|
|
+ layout.addWidget(QLabel('Have at least 1 horizontal or vertical guide.'))
|
|
|
+ else:
|
|
|
+ layout.addWidget(QLabel('Have at least 2 horizontal and 2 vertical guides.'))
|
|
|
+ dialog.setLayout(layout)
|
|
|
+ dialog.exec_()
|
|
|
+ return
|
|
|
+
|
|
|
+ # Find segments, row by row.
|
|
|
+ startV = vPoints.pop(0)
|
|
|
+ startH = hPoints.pop(0)
|
|
|
+ prevV = startV
|
|
|
+ count = 1
|
|
|
+ heightLimit = int(self.heightInput.text())
|
|
|
+ widthLimit = int(self.widthInput.text())
|
|
|
+ for v in vPoints:
|
|
|
+ segHeight = v - prevV
|
|
|
+ if segHeight >= heightLimit:
|
|
|
+ prevH = startH
|
|
|
+ for h in hPoints:
|
|
|
+ segWidth = h - prevH
|
|
|
+ if segWidth >= widthLimit:
|
|
|
+ # Copy image data to new layer.
|
|
|
+ segmentData = layer.pixelData(prevH, prevV, segWidth, segHeight)
|
|
|
+ newLayer = doc.createNode('seg' + str(count), 'paintLayer')
|
|
|
+ newLayer.setPixelData(segmentData, prevH, prevV, segWidth, segHeight)
|
|
|
+ root.addChildNode(newLayer, None)
|
|
|
+ count += 1
|
|
|
+ prevH = h
|
|
|
+ prevV = v
|
|
|
+
|