「Krita」で「Python」をプログラミングしてはじめての画像を描こう

第3回の今回は、「Krita」で「Python」をプログラミングして、はじめてのドキュメントやイメージ、レイヤーを作る解説をします。

大西 武 (オオニシ タケシ)

2024年7月23日 6:30

はじめに

第1回で予告した通り、今回から早速実際に「Python」をプログラミングして「Krita」にグラデーション矩形とランダムな正方形を描画していきます。

その前に、まずウィンドウにイメージを描画するテンプレート(ひな形)を作っていきます。そのためにはドキュメントを開いたり、開いたドキュメントを選択したりしてそのドキュメントでレイヤーを作り、そのレイヤーにイメージを描画します。

なお、KritaでPythonをスクリプティングするための「スクリプター」の使い方は第1回を参考にしてください。今後の連載では、スクリプターの使い方に関する説明は割愛させていただきます。

ドキュメントとイメージとレイヤー

ドキュメントとは、絵を描くウィンドウのことです。KritaではMDI(マルチドキュメントインターフェース)で複数のドキュメントを並行して処理できます。

イメージとは、ドット絵を描いたことがある方なら分かると思いますが、色の付いたドット(ピクセル、点)をたくさん描いた画像のことです。コンピュータのイメージはドットの集まりです。

一方のレイヤーとは、アニメでいうセル画のような透明な層のことです。例えば顔の絵を描くときレイヤーごとに皮膚、目、鼻、口、耳、眉毛、髪の毛などと層を重ねて描くと、それぞれのパーツの位置を変えたり形を変えたりなどが簡単にできるので、コンピュータで絵を描く場合には必須の機能です。

ドキュメントの作成

次のサンプルスクリプト「doc.py」をスクリプティングして「▷(実行)」ボタンを押すと、図1のような真っ白なイメージのレイヤーのあるドキュメントウィンドウが現れます。どのような幅高さのドキュメントを作っても、最初にウィンドウが表示されるときは描画領域がウィンドウのサイズにピッタリ拡大縮小されて表示されます。

図1:ドキュメントの作成

・サンプルスクリプト「doc.py」
# ドキュメントの作成
doc = Krita.instance().createDocument(256,256, "Document name", "RGBA", "U8", "", 300.0)
Krita.instance().activeWindow().addView(doc)

【サンプルスクリプトの解説】
「createDocument」メソッドで256x256pxのサイズで"Document name"という名前の赤(Red)、緑(Green)、青(Blue)、アルファ(Alpha)がU8(符号なし8ビット)、プロファイルなし、解像度300のドキュメントを作成します。
アクティブなウィンドウのビューにドキュメントを追加(addView)します。

イメージの作成

次のサンプルスクリプト「image.py」のように追記して「▷(実行)」ボタンを押すと、また図2のような真っ白なイメージのレイヤーのあるドキュメントウィンドウが現れます。透明な256x256pxのイメージを作ったのですが、まだレイヤーに描画していないため何も起こらず真っ白なままです。

ドキュメントの作成は「create_doc」関数にまとめました。この関数では戻り値でドキュメントのインスタンスを受け渡しします。

図2:イメージの作成

・サンプルスクリプト「image.py」
# モジュール
from PyQt5.Qt import *
# ドキュメントの作成
def create_doc(width,height):
  doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0)
  Krita.instance().activeWindow().addView(doc)
  return doc
# ドキュメントの取得
doc = create_doc(256,256)
# イメージの用意
pixmap = QPixmap(doc.bounds().size())
pixmap.fill(QColor(Qt.transparent))
img = pixmap.toImage()

【サンプルスクリプトの解説】
「import」文で「PyQt5」モジュールを読み込みます。
「pixmap = QPixmap(doc.bounds().size())」は本来なら「pixmap = PyQt5.Qt.QPixmap(doc.bounds().size())」と書くところ、import文で「*」と書いているので「PyQt5.Qt」を書かなくてもこのモジュールの機能が使えます。
「QPixmap」クラスのインスタンスでイメージを生成し、透明で塗りつぶし(fill)、「QImage」形式に変換します。

レイヤーの作成

次のサンプルスクリプト「layer.py」のように追記して「▷(実行)」ボタンを押すと、図3のような透明なイメージのレイヤーのあるドキュメントウィンドウが現れます。ここで透明なイメージをアクティブなレイヤーに描画しました。

これで、ひと通りドキュメントを作成してイメージを描き、レイヤーにイメージをセットするところまでできました。イメージの用意は「draw_img」関数にまとめました。今後、たいていこういった名前の関数内で中心となる絵をプログラミングして形を描きます。

図3:レイヤーの作成

・サンプルスクリプト「layer.py」
# モジュール
from PyQt5.Qt import *
# ドキュメントの作成
def create_doc(width,height):
  doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0)
  Krita.instance().activeWindow().addView(doc)
  return doc
# イメージの用意
def draw_img(doc):
  pixmap = QPixmap(doc.bounds().size())
  pixmap.fill(QColor(Qt.transparent))
  return pixmap.toImage()
# ドキュメントの取得
doc = create_doc(256,256)
# イメージの取得
img = draw_img(doc)
# 修正箇所
root = doc.rootNode()
# 修正箇所
layer = root.childNodes()[0]
# イメージのバイト長の検証
if img.sizeInBytes() == 4 * layer.channels()[0].channelSize() *  doc.width() * doc.height():
  ptr = img.bits()
  ptr.setsize(img.byteCount())
  layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, doc.width() , doc.height()) 
else:
  print('Error')
# リフレッシュ
doc.refreshProjection()

【サンプルスクリプトの解説】
アクティブなレイヤーを取得します。他にもレイヤー名でアクティブとは限らないレイヤーも取得できます。
「if」文でイメージのバイト長が4×チャンネルサイズ×幅×高さと等しいか検証します。等しくなければ'Error'をターミナルに表示します。
イメージの幅高さと同じ大きさでレイヤーの中にイメージを描画します。
ドキュメントをリフレッシュ(refreshProjection)することでレイヤーへの描画を反映します。

テンプレートの完成

次のサンプルスクリプト「template.py」のように書き換えて「▷(実行)」ボタンを押すと、また透明なイメージのレイヤーのあるドキュメントウィンドウが現れます。プログラミングはテンプレートのような型になるスクリプトの部分まで深く理解していなくても大丈夫です。自分が書き足すスクリプトについてだけ理解していれば、他は後から何となく分かってくるものです。

レイヤーのセットは「set_layer」関数にまとめました。このテンプレートでKritaを使った基本的な描画処理はひと通り完成です。あとは主にこのテンプレートに肉付けしていくだけです。

・サンプルスクリプト「template.py」
# モジュール
from PyQt5.Qt import *
# ドキュメントの作成
def create_doc(width,height):
  doc = Krita.instance().createDocument(width,height, "Document name", "RGBA", "U8", "", 300.0)
  Krita.instance().activeWindow().addView(doc)
  return doc
# グラデーションの描画
def draw_img(doc):
  pixmap = QPixmap(doc.bounds().size())
  pixmap.fill(QColor(Qt.transparent))
  return pixmap.toImage()
# レイヤーのセット
def set_layer(doc,img):
  # 修正箇所
  root = doc.rootNode()
  # 修正箇所
  layer = root.childNodes()[0]
  if img.sizeInBytes() == 4 * layer.channels()[0].channelSize() *  doc.width() * doc.height():
    ptr = img.bits()
    ptr.setsize(img.byteCount())
    layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, doc.width() , doc.height()) 
  else:
    print('Error')
# メイン関数(main()という関数名にしたいところだが既に使われている)
def begin_draw():
  doc = create_doc(256,256)
  img = draw_img(doc)
  set_layer(doc,img)
  # リフレッシュ
  doc.refreshProjection()
# メイン関数の呼び出し
begin_draw()

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored

ClickHouse Cloudが実現した広告運用SaaS「ATOM」のデータ基盤革新、パフォーマンスを向上しながら、運用負荷とコストを削減 11月11日 6:20 AIで開発が加速する時代、品質保証はどう変わる? Tricentisが示すAIエージェント活用の実像 11月9日 6:45 悪用までの猶予が「1分」に迫るフロンティアAI時代に開発と本番を貫く脆弱性管理が必要 10月29日 8:30 欧州CRA対応とSBOM運用。AI生成コードのOSS混入問題の対策とは ――スニペット単位で検出する「FossID」「Insignary Clarity」によるSBOM運用 9月16日 6:40 PostgreSQLからTiDBへ、稼働中SaaSで進める段階的DB移行の実践と再設計への展望 9月7日 6:30 機能安全開発におけるGoogleTestの活用を支援!「Parasoft C/C++test CT」で実現する効率化&AI連携 4月14日 6:30 【CNDW2025】Grafanaが明かす「オブザーバビリティの哲学」ー最小限の労力で実用的なインサイトを得るには 1月23日 6:30 【CNDW2025】プロダクト急増に備える基盤刷新 ーウェルスナビがECSからEKSへの移行で得た知見とは 1月15日 6:30 【CNDW2025】250環境を5人で運用、構築時間は30分に ーKINTOテクノロジーズが語るインフラ基盤組織の作り方 2025年12月18日 6:30 新たな自動化で熱視線! AIエージェントの「推論能力」を支える2つのコンポーネントとは? 2025年11月28日 6:30