page_slicer_docker.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from krita import DockWidget
  2. from PyQt5.QtWidgets import QWidget, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
  3. DOCKER_TITLE = 'Page Slicer'
  4. class PageSlicer(DockWidget):
  5. def __init__(self):
  6. super().__init__()
  7. self.setWindowTitle(DOCKER_TITLE)
  8. widget = QWidget()
  9. layout = QVBoxLayout()
  10. widget.setLayout(layout)
  11. layout.addSpacing(10)
  12. layout.addWidget(QLabel('Slice up an image into segments using page guides.'))
  13. # Thresholds for ignoring segments. Defaults to 100px in both dimensions.
  14. # limitModes = ['<', '>']
  15. self.widthInput = QLineEdit('100')
  16. # self.widthLimitMode = QComboBox()
  17. # self.widthLimitMode.addItems(limitModes)
  18. self.heightInput = QLineEdit('100')
  19. # self.heightLimitMode = QComboBox()
  20. # self.heightLimitMode.addItems(limitModes)
  21. layout.addWidget(QLabel('Segment-Ignore Thresholds:'))
  22. row1 = QHBoxLayout()
  23. row1.addWidget(QLabel('Width <'))
  24. # row1.addWidget(self.widthLimitMode)
  25. row1.addWidget(self.widthInput)
  26. row1.addWidget(QLabel('px'))
  27. layout.addLayout(row1)
  28. row2 = QHBoxLayout()
  29. row2.addWidget(QLabel('Height <'))
  30. # row2.addWidget(self.heightLimitMode)
  31. row2.addWidget(self.heightInput)
  32. row2.addWidget(QLabel('px'))
  33. layout.addLayout(row2)
  34. # Use image bounds or not
  35. self.useImageBoundsCheck = QCheckBox('Use Image Bounds')
  36. self.useImageBoundsCheck.setChecked(False)
  37. layout.addWidget(self.useImageBoundsCheck)
  38. # Button!
  39. layout.addSpacing(20)
  40. goButton = QPushButton("Slice")
  41. goButton.setIcon( Krita.instance().icon('animation_play') )
  42. layout.addWidget(goButton)
  43. # Add a stretch to prevent the rest of the content from stretching.
  44. layout.addStretch()
  45. # Add widget to the docker.
  46. self.setWidget(widget)
  47. # Hook up the action to the button.
  48. goButton.clicked.connect( self.slicePage )
  49. # notifies when views are added or removed
  50. # 'pass' means do not do anything
  51. def canvasChanged(self, canvas):
  52. pass
  53. ##########
  54. # Slots
  55. ##########
  56. # Actually slices the page.
  57. def slicePage(self, e):
  58. # Get the current layer.
  59. doc = Krita.instance().activeDocument()
  60. root = doc.rootNode()
  61. layer = doc.activeNode()
  62. if layer.type() != 'paintlayer':
  63. dialog = QDialog()
  64. dialog.setWindowTitle("Paint Layer Required")
  65. layout = QVBoxLayout()
  66. layout.addWidget(QLabel('Page slicer only works on paint layers. Please select one.'))
  67. dialog.setLayout(layout)
  68. dialog.exec_()
  69. return
  70. # Get horizontal and vertical points.
  71. # Horizontal guides provide the Vertical y-axis points and vice versa.
  72. vPoints = doc.horizontalGuides()
  73. hPoints = doc.verticalGuides()
  74. # Add the edges of the image for segment calculation, if needed.
  75. useImageBounds = self.useImageBoundsCheck.checkState()
  76. if useImageBounds:
  77. vPoints.insert(0, 0)
  78. vPoints.append(doc.height())
  79. hPoints.insert(0, 0)
  80. hPoints.append(doc.width())
  81. # Sanitize points. Guide values are floats, and may contain unexpected
  82. # extra fractional values.
  83. vPoints = [round(v) for v in vPoints]
  84. vPoints.sort()
  85. hPoints = [round(h) for h in hPoints]
  86. hPoints.sort()
  87. # Check that there are enough vertical and horizontal points.
  88. # When using image bounds there must be at least 1 guide (V or H).
  89. # When not using image bounds, there must be at least 4 guides (2V, 2H).
  90. imageBoundsLimitFail = True if len(vPoints) + len(hPoints) < 5 else False
  91. nonImageBoundsLimitFail = True if len(vPoints) < 2 or len(hPoints) < 2 else False
  92. if (useImageBounds and imageBoundsLimitFail) or nonImageBoundsLimitFail:
  93. dialog = QDialog()
  94. dialog.setWindowTitle("Insufficient Guides!")
  95. layout = QVBoxLayout()
  96. if useImageBounds:
  97. layout.addWidget(QLabel('Have at least 1 horizontal or vertical guide.'))
  98. else:
  99. layout.addWidget(QLabel('Have at least 2 horizontal and 2 vertical guides.'))
  100. dialog.setLayout(layout)
  101. dialog.exec_()
  102. return
  103. # Find segments, row by row.
  104. startV = vPoints.pop(0)
  105. startH = hPoints.pop(0)
  106. prevV = startV
  107. count = 1
  108. heightLimit = int(self.heightInput.text())
  109. widthLimit = int(self.widthInput.text())
  110. for v in vPoints:
  111. segHeight = v - prevV
  112. if segHeight >= heightLimit:
  113. prevH = startH
  114. for h in hPoints:
  115. segWidth = h - prevH
  116. if segWidth >= widthLimit:
  117. # Copy image data to new layer.
  118. segmentData = layer.pixelData(prevH, prevV, segWidth, segHeight)
  119. newLayer = doc.createNode('seg' + str(count), 'paintLayer')
  120. newLayer.setPixelData(segmentData, prevH, prevV, segWidth, segHeight)
  121. root.addChildNode(newLayer, None)
  122. count += 1
  123. prevH = h
  124. prevV = v