263 lines
9.8 KiB
Python
263 lines
9.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
import json
|
|
import os
|
|
import traceback
|
|
|
|
from krita import Krita
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtWidgets import (QApplication, QButtonGroup, QCheckBox, QDialog,
|
|
QDialogButtonBox, QFileDialog, QFormLayout, QGroupBox,
|
|
QHBoxLayout, QLabel, QLineEdit, QListWidget,
|
|
QMessageBox, QPlainTextEdit, QProgressBar, QPushButton,
|
|
QRadioButton, QSpinBox, QStackedWidget, QVBoxLayout,
|
|
QWidget, QAbstractItemView)
|
|
|
|
from .exporter import ExportOptions, export_document, export_files
|
|
|
|
SETTINGS_GROUP = "kra2svg_inkscape"
|
|
|
|
|
|
class ExportDialog(QDialog):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("KRA → SVG für Inkscape")
|
|
self.resize(640, 620)
|
|
self.app = Krita.instance()
|
|
|
|
lay = QVBoxLayout(self)
|
|
|
|
# ---- Modus ----
|
|
mode_box = QHBoxLayout()
|
|
self.rb_single = QRadioButton("Aktives Dokument")
|
|
self.rb_batch = QRadioButton("Batch (mehrere .kra-Dateien)")
|
|
grp = QButtonGroup(self)
|
|
grp.addButton(self.rb_single)
|
|
grp.addButton(self.rb_batch)
|
|
mode_box.addWidget(self.rb_single)
|
|
mode_box.addWidget(self.rb_batch)
|
|
mode_box.addStretch()
|
|
lay.addLayout(mode_box)
|
|
|
|
self.stack = QStackedWidget()
|
|
lay.addWidget(self.stack)
|
|
|
|
# Einzel
|
|
w_single = QWidget()
|
|
f = QFormLayout(w_single)
|
|
self.ed_out = QLineEdit()
|
|
b = QPushButton("…")
|
|
b.clicked.connect(self._browse_out)
|
|
row = QHBoxLayout()
|
|
row.addWidget(self.ed_out)
|
|
row.addWidget(b)
|
|
f.addRow("SVG-Datei:", row)
|
|
self.stack.addWidget(w_single)
|
|
|
|
# Batch
|
|
w_batch = QWidget()
|
|
v = QVBoxLayout(w_batch)
|
|
v.setContentsMargins(0, 0, 0, 0)
|
|
self.lst = QListWidget()
|
|
self.lst.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
|
v.addWidget(self.lst)
|
|
row = QHBoxLayout()
|
|
b_add = QPushButton("Dateien hinzufügen…")
|
|
b_add.clicked.connect(self._add_files)
|
|
b_dir = QPushButton("Ordner hinzufügen…")
|
|
b_dir.clicked.connect(self._add_folder)
|
|
b_rm = QPushButton("Entfernen")
|
|
b_rm.clicked.connect(self._remove_files)
|
|
for x in (b_add, b_dir, b_rm):
|
|
row.addWidget(x)
|
|
row.addStretch()
|
|
v.addLayout(row)
|
|
f2 = QFormLayout()
|
|
self.ed_dir = QLineEdit()
|
|
b = QPushButton("…")
|
|
b.clicked.connect(self._browse_dir)
|
|
row = QHBoxLayout()
|
|
row.addWidget(self.ed_dir)
|
|
row.addWidget(b)
|
|
f2.addRow("Ausgabeordner:", row)
|
|
v.addLayout(f2)
|
|
self.stack.addWidget(w_batch)
|
|
|
|
self.rb_single.toggled.connect(lambda on: self.stack.setCurrentIndex(0 if on else 1))
|
|
|
|
# ---- Optionen ----
|
|
box = QGroupBox("Optionen")
|
|
ob = QFormLayout(box)
|
|
self.cb_skip = QCheckBox("Unsichtbare Ebenen überspringen")
|
|
self.cb_vec = QCheckBox("Vektorebenen als SVG-Objekte erhalten")
|
|
self.cb_layers = QCheckBox("Krita-Gruppen als Inkscape-Ebenen (statt Gruppen)")
|
|
self.cb_clip = QCheckBox("Auf Leinwand beschneiden")
|
|
self.cb_phys = QCheckBox("Physische Größe (mm) aus Dokument-DPI")
|
|
for cb in (self.cb_skip, self.cb_vec, self.cb_layers, self.cb_clip, self.cb_phys):
|
|
ob.addRow(cb)
|
|
self.sp_margin = QSpinBox()
|
|
self.sp_margin.setRange(0, 512)
|
|
self.sp_margin.setSuffix(" px")
|
|
ob.addRow("Rand um PNGs:", self.sp_margin)
|
|
prow = QHBoxLayout()
|
|
self.rb_embed = QRadioButton("PNGs einbetten")
|
|
self.rb_assets = QRadioButton("Separate Asset-Dateien (<name>_assets/)")
|
|
g2 = QButtonGroup(self)
|
|
g2.addButton(self.rb_embed)
|
|
g2.addButton(self.rb_assets)
|
|
prow.addWidget(self.rb_embed)
|
|
prow.addWidget(self.rb_assets)
|
|
ob.addRow("Bilder:", prow)
|
|
lay.addWidget(box)
|
|
|
|
# ---- Log ----
|
|
self.progress = QProgressBar()
|
|
self.progress.setVisible(False)
|
|
lay.addWidget(self.progress)
|
|
self.log_view = QPlainTextEdit()
|
|
self.log_view.setReadOnly(True)
|
|
self.log_view.setMinimumHeight(160)
|
|
lay.addWidget(QLabel("Protokoll:"))
|
|
lay.addWidget(self.log_view, 1)
|
|
|
|
bb = QDialogButtonBox()
|
|
self.btn_export = bb.addButton("Exportieren", QDialogButtonBox.AcceptRole)
|
|
bb.addButton(QDialogButtonBox.Close)
|
|
self.btn_export.clicked.connect(self._run)
|
|
bb.rejected.connect(self.reject)
|
|
lay.addWidget(bb)
|
|
|
|
self._load_settings()
|
|
|
|
# ---- Einstellungen ---------------------------------------------------- #
|
|
def _options(self):
|
|
return ExportOptions(
|
|
skip_invisible=self.cb_skip.isChecked(),
|
|
keep_vectors=self.cb_vec.isChecked(),
|
|
embed_png=self.rb_embed.isChecked(),
|
|
margin=self.sp_margin.value(),
|
|
groups_as_layers=self.cb_layers.isChecked(),
|
|
clip_to_canvas=self.cb_clip.isChecked(),
|
|
physical_size=self.cb_phys.isChecked(),
|
|
)
|
|
|
|
def _load_settings(self):
|
|
try:
|
|
o = ExportOptions.from_dict(json.loads(
|
|
self.app.readSetting(SETTINGS_GROUP, "options", "{}") or "{}"))
|
|
except ValueError:
|
|
o = ExportOptions()
|
|
self.cb_skip.setChecked(o.skip_invisible)
|
|
self.cb_vec.setChecked(o.keep_vectors)
|
|
self.cb_layers.setChecked(o.groups_as_layers)
|
|
self.cb_clip.setChecked(o.clip_to_canvas)
|
|
self.cb_phys.setChecked(o.physical_size)
|
|
self.sp_margin.setValue(o.margin)
|
|
(self.rb_embed if o.embed_png else self.rb_assets).setChecked(True)
|
|
self.ed_dir.setText(self.app.readSetting(SETTINGS_GROUP, "batch_dir", ""))
|
|
|
|
doc = self.app.activeDocument()
|
|
if doc is not None:
|
|
self.rb_single.setChecked(True)
|
|
src = doc.fileName()
|
|
if src:
|
|
self.ed_out.setText(os.path.splitext(src)[0] + ".svg")
|
|
else:
|
|
self.rb_single.setEnabled(False)
|
|
self.rb_batch.setChecked(True)
|
|
self.stack.setCurrentIndex(0 if self.rb_single.isChecked() else 1)
|
|
|
|
def _save_settings(self):
|
|
self.app.writeSetting(SETTINGS_GROUP, "options", json.dumps(self._options().to_dict()))
|
|
self.app.writeSetting(SETTINGS_GROUP, "batch_dir", self.ed_dir.text())
|
|
|
|
# ---- Dateiauswahl ----------------------------------------------------- #
|
|
def _browse_out(self):
|
|
p, _ = QFileDialog.getSaveFileName(self, "SVG speichern", self.ed_out.text(),
|
|
"SVG (*.svg)")
|
|
if p:
|
|
if not p.lower().endswith(".svg"):
|
|
p += ".svg"
|
|
self.ed_out.setText(p)
|
|
|
|
def _browse_dir(self):
|
|
d = QFileDialog.getExistingDirectory(self, "Ausgabeordner", self.ed_dir.text())
|
|
if d:
|
|
self.ed_dir.setText(d)
|
|
|
|
def _existing(self):
|
|
return {self.lst.item(i).text() for i in range(self.lst.count())}
|
|
|
|
def _add_files(self):
|
|
files, _ = QFileDialog.getOpenFileNames(self, ".kra-Dateien wählen", "",
|
|
"Krita (*.kra)")
|
|
have = self._existing()
|
|
for p in files:
|
|
if p not in have:
|
|
self.lst.addItem(p)
|
|
|
|
def _add_folder(self):
|
|
d = QFileDialog.getExistingDirectory(self, "Ordner mit .kra-Dateien")
|
|
if not d:
|
|
return
|
|
have = self._existing()
|
|
for name in sorted(os.listdir(d)):
|
|
p = os.path.join(d, name)
|
|
if name.lower().endswith(".kra") and p not in have:
|
|
self.lst.addItem(p)
|
|
|
|
def _remove_files(self):
|
|
for it in self.lst.selectedItems():
|
|
self.lst.takeItem(self.lst.row(it))
|
|
|
|
# ---- Export ----------------------------------------------------------- #
|
|
def _log(self, msg):
|
|
self.log_view.appendPlainText(msg)
|
|
QApplication.processEvents()
|
|
|
|
def _run(self):
|
|
self._save_settings()
|
|
opts = self._options()
|
|
self.log_view.clear()
|
|
self.btn_export.setEnabled(False)
|
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
try:
|
|
if self.rb_single.isChecked():
|
|
self._run_single(opts)
|
|
else:
|
|
self._run_batch(opts)
|
|
except Exception:
|
|
self._log(traceback.format_exc())
|
|
QMessageBox.critical(self, "Export fehlgeschlagen",
|
|
"Details stehen im Protokoll.")
|
|
finally:
|
|
QApplication.restoreOverrideCursor()
|
|
self.btn_export.setEnabled(True)
|
|
self.progress.setVisible(False)
|
|
|
|
def _run_single(self, opts):
|
|
doc = self.app.activeDocument()
|
|
if doc is None:
|
|
QMessageBox.warning(self, "Kein Dokument", "Es ist kein Dokument geöffnet.")
|
|
return
|
|
out = self.ed_out.text().strip()
|
|
if not out:
|
|
QMessageBox.warning(self, "Ziel fehlt", "Bitte eine SVG-Datei angeben.")
|
|
return
|
|
warnings = export_document(doc, out, opts, self._log)
|
|
self._log("Fertig. %d Warnung(en)." % warnings)
|
|
|
|
def _run_batch(self, opts):
|
|
files = [self.lst.item(i).text() for i in range(self.lst.count())]
|
|
out_dir = self.ed_dir.text().strip()
|
|
if not files or not out_dir:
|
|
QMessageBox.warning(self, "Angaben fehlen",
|
|
"Bitte Dateien und einen Ausgabeordner wählen.")
|
|
return
|
|
self.progress.setRange(0, len(files))
|
|
self.progress.setValue(0)
|
|
self.progress.setVisible(True)
|
|
res = export_files(files, out_dir, opts, self._log,
|
|
progress=lambda i, n: self.progress.setValue(i))
|
|
ok = sum(1 for r in res if r[1])
|
|
self._log("Fertig: %d von %d Datei(en) exportiert." % (ok, len(res)))
|