I am trying to take json data along with an image and load it into Krita as a brush preset but I have come across an issue where the Resource class cannot be instantiated.
Does anyone happen to know if Krita supports creating brush presets from scratch using python?
I came across this topic but it does not shed any light on the issue:
I am using Krita 5.2.2 on Windows 10.
Krita 5.2.2 itself is using Python 3.10.7.
Here is the snippet that produces the error:
from krita import Krita, Resource, Preset
_krita = Krita.instance()
# TypeError: PyKrita.krita.Resource cannot be instantiated or sub-classed
resource_instance = Resource() # <-- error cannot instantiate Resource
# this would be the created brush preset but it needs a Resource object to initialize
# how can I provide it with a custom resource?
preset = Preset(resource_instance)
You can’t just take random JSON data and load it as a brush preset, it must be in the XML format Krita uses?
There’s nothing in the API to help with this, you have to do it manually, which requires understanding of the XML structure of the brush settings. There’s no guide for that, you have to look at existing brush XML or read Krita’s source code to figure it out.
The easiest way is to take an existing preset and modify it, like this:
from krita import *
import xml.etree.ElementTree as ET
view = Krita.instance().activeWindow().activeView()
preset = Preset(view.currentBrushPreset())
presetXMLString = preset.toXML()
root = ET.fromstring(presetXMLString)
# do things with the XML here
root.set("name", "Test Brush")
presetXMLString = ET.tostring(root, encoding="unicode")
preset.fromXML(presetXMLString)
If you really want to create a brush preset from scratch, you need to create the XML settings and put them as the text metadata (zTXt or iTXt) of the thumbnail PNG, and save it with the .kpp file extension.
I haven’t tested editing resources database using QtSql, but in theory it should work…
But, yes you still need to write XML .kpp files like freyalupen said.
from PyQt5.QtSql import QSqlTableModel
from PyQt5.QtWidgets import QWidget, QTableView, QVBoxLayout
class MyResourceEditor(QWidget):
"""
use QSqlDatabase.tables() to get available table names...
table names:
version_information
sqlite_sequence
storage_types
resource_types
storages
tags
resources
versioned_resources
resource_tags
metadata
tags_storages
tag_translations
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
layout = QVBoxLayout()
self.setLayout(layout)
model = QSqlTableModel()
model.setEditStrategy(QSqlTableModel.OnManualSubmit)
model.setTable('resources')
model.select()
view = QTableView()
view.setModel(model)
layout.addWidget(view)
my_editor = MyResourceEditor()
my_editor.show()
Thank you for the reply!
I’ll take a look to see if that will work.
Does this mean the changes made to the currentBrushPreset would overwrite the currentBrushPreset’s settings? Or, would saving out the changed xml settings with the image as a .kpp mean that, it is loaded in as a separate brush preset the next time Krita opens?
Here is a demo of changing the brush preset name using xml and a QImage:
import os
from PyQt5 import QtGui
from xml.etree import ElementTree
kpp_path = r"path\to\kpp_file.kpp"
png_path = r"path\to\png_file.png"
# open existing .kpp file
kpp_image = QtGui.QImage(kpp_path)
# open existing .png file
new_image = QtGui.QImage(png_path)
# update brush preset settings
root = ElementTree.fromstring(kpp_image.text("preset"))
root.set("name", "b) new brush name")
updated_preset = ElementTree.tostring(root, encoding='unicode')
# transfer the preset metadata from the
# opened .kpp to the opened .png
new_image.setText("version", kpp_image.text("version"))
new_image.setText("preset", updated_preset)
# save the .png with the updated metadata under a new name
new_image.save(png_path + "__.png")
# rename the saved .png to a .kpp file
os.rename(png_path + "__.png", png_path + ".kpp")
The resulting .kpp file can then be loaded into Krita.