For sake of now upgrading my plugin CBT to be more efficient - Im now drafting for and studying this.
Im pretty sure i will be able to implement smearing/dulling toggle.
Oth. Would it be possible to create a mini brush editor. a condensed brush editor for quick brush settings that would still link to the bigger editor for more complex stuff but allows editing for more mundane things. 
1 Like
Seems like it is possible to temporarily modify the current preset using this. But it requires parsing the XML, which… honestly, I have no idea how to do correctly, I would just resort to using a regular expression to parse it… (should be easy to do with matching and substitution, but I can’t remember how to do that right now).
I tried to parse it using Python’s ElementTree, but it only works for newly saved presets, old ones give an error. So it’s not useful.
Example ElementTree-using preset modifying code
# Example code to modify the diameter, only works on new presets...
from krita import *
import xml.etree.ElementTree as ET
view = Krita.instance().activeWindow().activeView()
preset = Preset(view.currentBrushPreset())
#print(preset.toXML())
presetXMLString = preset.toXML()
root = ET.fromstring(presetXMLString)
for param in root.findall('param'):
name = param.get('name')
if (name == "diameter"):
#type = param.text
#print("diameter:",type)
param.text = "7.77"
presetXMLString = ET.tostring(root, encoding="unicode")
preset.fromXML(presetXMLString)
It might be possible using QXmlStreamReader/Writer, but I don’t really understand how they work.
But as long as you can modify the XML in some way, you can change any brush setting. So it would be a better way than UI-based hacks.
2 Likes
Thank you. I have read the xml for random brushes to see what options are there.
I can’t seem to determine the xml name for Overlay Mode.
on other seems likes it’s now possible to build a mini docker with some of the more basic feature of brush editing.
[fade value, switching to square/blur/solid tips, turning pressure on/off]
it seems that even curve values are here.
I have too many ideas, gotta pare it down for now and do some minor test here and there.
Overlay Mode is called “MergedPaint”. You can find the setting names in the source code under /plugins/paintops/, in the KisXXOptionData.cpp files.
But yes, the XML is where every single brush setting is stored, that’s what gets saved into a .kpp file. Even brushtips and patterns can be stored in there.
1 Like
The easiest thing to do is take something as-is, then output the xml, then do the changes in the gui and output the xml. Then use a diff/comparison of the 2 texts, thus you can easily see the changes
I did some brain storming; I gonna try this as sort of project after transferring CBT to using Preset.
if i finish it I’ll publish but this is more of a let me test this for myself to learn sort of thing.
at the grand scheme of things something like this is probably better of coded in the source - rather than as python plugin.
4 Likes
so im testing some and manage do to basic brush setting which are under brush definition but need the internal text to be also converted to xml
<param name="brush_definition" type="string">
<![CDATA[<Brush spacing="0.1" randomness="0" density="1" BrushVersion="2" angle="0" type="auto_brush" useAutoSpacing="1" autoSpacingCoeff="0.6"> <MaskGenerator spikes="2" diameter="60" ratio="1" vfade="0.87" antialiasEdges="0" hfade="0.87" type="circle" id="default"/> </Brush> ]]>
</param>
I made this based on your sample; only works for auto tips
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)
for param in root.findall('param'):
name = param.get('name')
# print(name)
if ( name == "brush_definition"):
brushdef =ET.fromstring( param.text)
brushopt = brushdef.find('MaskGenerator')
# print(brushopt.get('diameter'))
brushopt.set('diameter', '250')
brushopt.set('hfade', '.50')
brushopt.set('vfade', '.50')
xmlbrushdef = ET.tostring(brushdef, encoding="unicode")
#print(xmlbrushdef)
#brushdef.fromXML(xmlbrushdef)
param.text = xmlbrushdef
presetXMLString = ET.tostring(root, encoding="unicode")
preset.fromXML(presetXMLString)
most of this section is under brush_definition;

This thread is probably just me experimenting this.
4 Likes
That wasn’t really correct, I just hadn’t looked into it properly. The actual issue is, the error can happen with brushes with embedded patterns.
For some reason “Texture/Pattern/PatternMD5” is basically saved in binary, which breaks the XML parser. (I’d come across this issue before and forgotten about it.)
The bit causing the issue can replaced with this code (before parsing the XML);
import re
pat = '<param type="string" name="Texture/Pattern/PatternMD5"><!\[CDATA\[(.+)\]\]></param>'
match = re.search(pat, presetXMLString)
md5sum = match.group(1)
pat2 = '<param type="string" name="Texture/Pattern/PatternMD5"><!\[CDATA\[%s\]\]></param>' % re.escape(md5sum)
def replfunc(m):
return '<param type="string" name="Texture/Pattern/PatternMD5"><![CDATA[%s]]></param>' % \
"TEMP"
presetXMLString = re.sub(pat2, replfunc, presetXMLString, count=1)
but the MD5 can’t be put back afterward, because Preset.fromXML() will complain it’s invalid. In theory that might break the pattern if you were to resave the preset afterward? But in practice, I’m not sure.
1 Like