Skip to content

Commit

Permalink
Add minimal palette export
Browse files Browse the repository at this point in the history
  • Loading branch information
evnlme committed Sep 25, 2024
1 parent 18315fa commit 6fa4e7d
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
Binary file added half_tone_selector/icc/sRGB-elle-V2-srgbtrc.icc
Binary file not shown.
71 changes: 71 additions & 0 deletions half_tone_selector/palette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import xml.etree.ElementTree as ET
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import List
from zipfile import ZipFile
from .state import HalfToneSet
from .color import convertColorSpace

mimetype = 'application/x-krita-palette'
iccPath = Path(__file__).resolve().parent / 'icc/sRGB-elle-V2-srgbtrc.icc'


def exportPalette(halfTones: List[HalfToneSet], name: str, path: Path) -> None:
with TemporaryDirectory() as temp:
_exportPalette(halfTones, name, path, temp)

def _exportPalette(halfTones: List[HalfToneSet], name: str, path: Path, temp: Path) -> None:
tempPath = Path(temp)
mimetypePath = tempPath / 'mimetype'
profilesPath = tempPath / 'profiles.xml'
colorsetPath = tempPath / 'colorset.xml'

with mimetypePath.open('w') as f:
f.write(mimetype)

profiles = ET.Element('Profiles')
profileAttrs = {
'colorModelId': 'RGBA',
'colorDepthId': 'U8',
'filename': iccPath.name,
'name': iccPath.name,
}
profile = ET.SubElement(profiles, 'Profile', profileAttrs)
with profilesPath.open('w') as f:
f.write(ET.tostring(profiles, encoding='unicode'))

colorsetAttrs = {
'name': name,
'version': '2.0',
'comment': '',
'rows': str(len(halfTones)),
'columns': '12',
}
colorset = ET.Element('ColorSet', colorsetAttrs)
for i, ht in enumerate(halfTones):
for j, t in enumerate(ht.tones):
rgb = convertColorSpace(t, 'Oklch', 'sRGB')
entryAttrs = {
'id': f'{12*i+j}',
'name': f'Color {12*i+j}',
'bitdepth': 'U8',
'spot': 'false',
}
colorsetEntry = ET.SubElement(colorset, 'ColorSetEntry', entryAttrs)
rgbElemAttrs = {
'space': iccPath.name,
'r': str(rgb[0]),
'g': str(rgb[1]),
'b': str(rgb[2]),
}
rgbElem = ET.SubElement(colorsetEntry, 'RGB', rgbElemAttrs)
positionAttrs = {'row': str(i), 'column': str(j)}
position = ET.SubElement(colorsetEntry, 'Position', positionAttrs)
with colorsetPath.open('w') as f:
f.write(ET.tostring(colorset, encoding='unicode'))

with ZipFile(path / f'{name}.kpl', 'w') as f:
f.write(filename=mimetypePath, arcname=mimetypePath.name)
f.write(filename=iccPath, arcname=iccPath.name)
f.write(filename=profilesPath, arcname=profilesPath.name)
f.write(filename=colorsetPath, arcname=colorsetPath.name)
87 changes: 87 additions & 0 deletions half_tone_selector/widget.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import math
import re
import krita as K # type: ignore
from pathlib import Path
from typing import Callable, Optional, List, Tuple
from .matrix import (
Vec,
Expand All @@ -19,6 +21,7 @@
qcolorToOklch,
qcolorToOklchFunc,
)
from .palette import exportPalette

def addLayout(
qlayout: Callable[[], K.QLayout],
Expand Down Expand Up @@ -428,6 +431,82 @@ def previewSettings(app: HalfToneSelectorApp) -> K.QWidget:
layout.setSpacing(5)
return widget

class ExportPaletteDialog(K.QWidget):
def __init__(self, halfTones: List[HalfToneSet]) -> None:
super().__init__()
self._halfTones = halfTones
self._isValidName = False
self._isPathSet = False
self._nameText = K.QLineEdit()
self._fileDialog = K.QFileDialog()
self._pathButton = K.QPushButton()
self._pathText = K.QLineEdit()
self._exportButton = K.QPushButton('Export')
self._cancelButton = K.QPushButton('Cancel')
self._configureLayout()
self._configureActions()

def _configureLayout(self) -> None:
mainLayout = K.QVBoxLayout()
mainLayout.setAlignment(K.Qt.AlignTop)
self.setLayout(mainLayout)

nameLayout = K.QHBoxLayout()
nameLayout.setContentsMargins(0, 0, 0, 0)
nameWidget = K.QWidget()
nameWidget.setLayout(nameLayout)
nameLayout.addWidget(K.QLabel('Name:'))
nameLayout.addWidget(self._nameText)
mainLayout.addWidget(nameWidget)

pathLayout = K.QHBoxLayout()
pathLayout.setContentsMargins(0, 0, 0, 0)
pathWidget = K.QWidget()
pathWidget.setLayout(pathLayout)
self._pathButton.setIcon(K.Krita.instance().icon('folder'))
self._pathText.setEnabled(False)
pathLayout.addWidget(self._pathButton)
pathLayout.addWidget(self._pathText)
mainLayout.addWidget(pathWidget)

exportLayout = K.QHBoxLayout()
exportLayout.setContentsMargins(0, 0, 0, 0)
exportWidget = K.QWidget()
exportWidget.setLayout(exportLayout)
exportLayout.addWidget(self._exportButton)
exportLayout.addWidget(self._cancelButton)
mainLayout.addWidget(exportWidget)

def _handleNameText(self) -> None:
text = self._nameText.text()
m = re.match(r'[\w-]+([ ]*[\w-]+)*', text)
self._isValidName = True if m else False
self._exportButton.setEnabled(self._isValidName and self._isPathSet)

def _handlePathButton(self) -> None:
if self._fileDialog.exec():
files = self._fileDialog.selectedFiles()
self._pathText.setText(files[0])
self._isPathSet = True
self._exportButton.setEnabled(self._isValidName and self._isPathSet)

def _handleExportButton(self) -> None:
name = self._nameText.text()
path = Path(self._pathText.text())
exportPalette(self._halfTones, name, path)
self.close()

def _handleCancelButton(self) -> None:
self.close()

def _configureActions(self) -> None:
self._nameText.textChanged.connect(self._handleNameText)
self._fileDialog.setFileMode(K.QFileDialog.Directory)
self._fileDialog.setOption(K.QFileDialog.ShowDirsOnly, True)
self._pathButton.clicked.connect(self._handlePathButton)
self._exportButton.clicked.connect(self._handleExportButton)
self._cancelButton.clicked.connect(self._handleCancelButton)

def settingsWidget(app: HalfToneSelectorApp) -> K.QWidget:
def create():
hts = generateColors(app.s)
Expand All @@ -436,13 +515,21 @@ def create():
createButton = K.QPushButton('Create half tones')
createButton.clicked.connect(create)

def export():
exportButton._dialog = ExportPaletteDialog(app.s.halfTones)
exportButton._dialog.show()

exportButton = K.QPushButton('Export to palette')
exportButton.clicked.connect(export)

widget, layout = addLayout(
qlayout=K.QVBoxLayout,
childWidgets=[
toneSettings(app),
samplingSettings(app),
previewSettings(app),
createButton,
exportButton,
])
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(5)
Expand Down

0 comments on commit 6fa4e7d

Please sign in to comment.