Just for fun, I converted the script to tiny plugin + moved drawing to image coordinates. There is a docker but it has now content, maybe there should be some options on how the guides should be placed. Also where the lines are drawn is something that I did select randomly.
How to use:
- copy & paste install script to Krita’s scripter (
tools → scripts → scripter)
- press run
- If scripter prints to output
manga_guides_plugin added Successfully (Krita restart required.), install was success, else there is an error message telling what did go wrong.
Tested on Kubuntu Krita 6.1.0-prealpha (with luck works on 5.*.*)
from pathlib import Path
from krita import Krita
app = Krita.instance()
user_pykrita_path = Path(app.getAppDataLocation()) / 'pykrita'
plugin_desktop_path = user_pykrita_path / 'manga_guides_plugin.desktop'
plugin_dir = user_pykrita_path / 'manga_guides_plugin'
plugin_init_path = plugin_dir / '__init__.py'
manga_guides_docker_path = plugin_dir / 'manga_guides_docker.py'
manga_guides_overlay_path = plugin_dir / 'manga_guides_overlay.py'
plugin_dir.mkdir(parents=True)
plugin_desktop_path.write_text("""\
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=manga_guides_plugin
X-Python-2-Compatible=false
X-Krita-Manual=readme.md
Name=Manga Guides Plugin
Comment=Manga Guides Plugin
""")
plugin_init_path.write_text("""\
PLUGIN_NAME = __name__
PLUGIN_NAME_SHORT = 'MAGP' # (Ma)nga (g)uides (P)lugin
PLUGIN_VERSION = __version__ = '0.1.0'
def register_plugin():
from .manga_guides_docker import MangaGuidesDocker
MangaGuidesDocker.register()
register_plugin()
""")
manga_guides_docker_path.write_text("""\
# importing Qt stuff from krita module (Qt5 <-> Qt6 combability)
from krita import (
Krita,
DockWidget,
DockWidgetFactoryBase,
DockWidgetFactory,
Qt,
QMdiArea,
QVBoxLayout,
QWidget)
from manga_guides_plugin import (
PLUGIN_NAME,
PLUGIN_NAME_SHORT,
PLUGIN_VERSION)
from .manga_guides_overlay import (
MangaGuidesOverlay,)
_APP = Krita.instance()
def find_q_view(target_view):
window = target_view.window()
q_window = window.qwindow()
mdi_area = q_window.findChild(QMdiArea)
for q_view, view in zip(mdi_area.subWindowList(), window.views()):
if view == target_view:
return q_view
raise RuntimeError('Something is wrong!')
def find_kis_canvases(parent):
for widget in parent.findChildren(QWidget):
if widget.inherits('KisCanvas2') or widget.inherits('KisOpenGLCanvas2'):
yield widget
def find_kis_canvas_controllers(parent):
for widget in parent.findChildren(QWidget):
if widget.inherits('KisCanvasController'):
yield widget
class MangaGuidesDocker(DockWidget):
@classmethod
def register(cls):
factory = DockWidgetFactory(
f'{PLUGIN_NAME}:manga_guides_docker',
DockWidgetFactoryBase.DockPosition.DockRight,
cls)
_APP.addDockWidgetFactory(factory)
def __init__(self):
super().__init__()
self.setWindowTitle('Manga Guides Docker')
self._init_ui()
def _init_ui(self):
content = QWidget()
layout = QVBoxLayout()
content.setLayout(layout)
self.setWidget(content)
def canvasChanged(self, canvas):
if not canvas or not canvas.view():
return # nothing to do
q_view = find_q_view(canvas.view())
kis_canvas_controller = next(find_kis_canvas_controllers(q_view), None)
if kis_canvas_controller.findChild(MangaGuidesOverlay) is None:
# missing a MangaGuidesOverlay -> adding one
manga_guides_overlay = MangaGuidesOverlay(canvas=canvas, parent=kis_canvas_controller)
kis_canvas_controller.installEventFilter(manga_guides_overlay)
manga_guides_overlay.show()
manga_guides_overlay.raise_()
""")
manga_guides_overlay_path.write_text("""\
# importing Qt stuff from krita module (Qt5 <-> Qt6 combability)
from krita import (
Krita,
Canvas,
Qt,
pyqtSlot as QSlot,
pyqtSignal as QSignal,
pyqtProperty as QProperty,
QRectF,
QPointF,
QEvent,
QPainter,
QPen,
QColor,
QTransform,
QWidget)
_APP = Krita.instance()
class MangaGuidesOverlay(QWidget):
def __init__(self, **kwargs):
self._canvas = None
super().__init__(**kwargs)
self.setAttribute(Qt.WidgetAttribute.WA_ForceDisabled, True)
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
self.setAutoFillBackground(False)
@QProperty(Canvas)
def canvas(self):
return self._canvas
@canvas.setter
def canvas(self, new_canvas):
self._canvas = new_canvas
def eventFilter(self, obj, event):
if event.type() in {QEvent.Type.Resize, QEvent.Type.LayoutRequest}:
q_canvas = self.parent()
self.setGeometry(q_canvas.rect())
return super().eventFilter(obj, event)
def widget_to_image_transform(self):
view = self._canvas.view()
to_canvas_transform = view.flakeToCanvasTransform()
to_image_transform = view.flakeToImageTransform()
inv_tr, _ = to_image_transform.inverted()
transform = inv_tr * to_canvas_transform
return transform
def paintEvent(self, event):
view = self._canvas.view()
document = view.document()
document_bounds = QRectF(document.bounds())
document_center = document_bounds.center()
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# notes:
# - painter origin is placed at center of document.
# - units are in document pixels.
transform = self.widget_to_image_transform()
painter.setTransform(transform)
debug_pen = QPen(QColor(255, 80, 80, 200), 5.0, Qt.PenStyle.SolidLine)
painter.setPen(debug_pen)
painter.drawRect(document_bounds)
# notes:
# line width of 0 makes pen a "cosmetic" aka. "hairline" pen ->
# width of draw line is always one pixel on screen.
pen_mark = QPen(QColor(0, 98, 255, 220), 0.0, Qt.PenStyle.SolidLine)
pen_bleed = QPen(QColor(0, 98, 255, 220), 0.0, Qt.PenStyle.SolidLine)
pen_safe = QPen(QColor(0, 98, 255, 220), 0.0, Qt.PenStyle.SolidLine)
pen_trim = QPen(QColor(0, 98, 255, 220), 0.0, Qt.PenStyle.SolidLine)
left_cross_center = QPointF(document_center)
left_cross_center.setX(document_bounds.left() + 32.0)
right_cross_center = QPointF(document_center)
right_cross_center.setX(document_bounds.right() - 32.0)
top_cross_center = QPointF(document_center)
top_cross_center.setY(document_bounds.top() + 32.0)
bottom_cross_center = QPointF(document_center)
bottom_cross_center.setY(document_bounds.bottom() - 32.0)
# Rectangles
bleed_rect = document_bounds.adjusted(48.0, 48.0, -48.0, -48.0)
trim_rect = document_bounds.adjusted(64.0, 64.0, -64.0, -64.0)
safe_rect = document_bounds.adjusted(128.0, 128.0, -128.0, -128.0)
painter.setPen(pen_mark)
self.draw_cross(painter, left_cross_center, 32.0)
self.draw_cross(painter, right_cross_center, 32.0)
self.draw_cross(painter, top_cross_center, 32.0)
self.draw_cross(painter, bottom_cross_center, 32.0)
painter.setPen(pen_mark)
painter.drawLine(
QPointF(bleed_rect.left() + 32.0, bleed_rect.top()),
QPointF(bleed_rect.left() + 32.0, bleed_rect.top() - 16.0))
painter.drawLine(
QPointF(bleed_rect.right() - 32.0, bleed_rect.top()),
QPointF(bleed_rect.right() - 32.0, bleed_rect.top() - 16.0))
painter.drawLine(
QPointF(bleed_rect.left(), bleed_rect.top() + 32.0),
QPointF(bleed_rect.left() - 16.0, bleed_rect.top() + 32.0))
painter.drawLine(
QPointF(bleed_rect.right(), bleed_rect.top() + 32.0),
QPointF(bleed_rect.right() + 16.0, bleed_rect.top() + 32.0))
painter.drawLine(
QPointF(bleed_rect.left() + 32.0, bleed_rect.bottom()),
QPointF(bleed_rect.left() + 32.0, bleed_rect.bottom() + 16.0))
painter.drawLine(
QPointF(bleed_rect.right() - 32.0, bleed_rect.bottom()),
QPointF(bleed_rect.right() - 32.0, bleed_rect.bottom() + 16.0))
painter.drawLine(
QPointF(bleed_rect.left(), bleed_rect.bottom() - 32.0),
QPointF(bleed_rect.left() - 16.0, bleed_rect.bottom() - 32.0))
painter.drawLine(
QPointF(bleed_rect.right(), bleed_rect.bottom() - 32.0),
QPointF(bleed_rect.right() + 16.0, bleed_rect.bottom() - 32.0))
painter.setPen(pen_bleed)
painter.drawRect(bleed_rect)
painter.setPen(pen_safe)
painter.drawRect(safe_rect)
painter.setPen(pen_trim)
painter.drawRect(trim_rect)
painter.end()
def draw_cross(self, painter, center, width):
half_width = 0.5 * width
painter.drawLine(
QPointF(center.x() - half_width, center.y()),
QPointF(center.x() + half_width, center.y()))
painter.drawLine(
QPointF(center.x(), center.y() - half_width),
QPointF(center.x(), center.y() + half_width))
""")
app.writeSetting('python', 'enable_manga_guides_plugin', 'true')
print('manga_guides_plugin added Successfully (Krita restart required.)')