...
 
Commits (6)
......@@ -69,6 +69,7 @@ bool ManagedColor::operator==(const ManagedColor &other) const
{
return d->color == other.d->color;
}
QColor ManagedColor::colorForCanvas(Canvas *canvas) const
{
QColor c = QColor(0,0,0);
......@@ -85,6 +86,22 @@ QColor ManagedColor::colorForCanvas(Canvas *canvas) const
return c;
}
ManagedColor *ManagedColor::fromQColor(const QColor &qcolor, Canvas *canvas)
{
KoColor c;
if (canvas && canvas->displayColorConverter() && canvas->displayColorConverter()->displayRendererInterface()) {
KoColorDisplayRendererInterface *converter = canvas->displayColorConverter()->displayRendererInterface();
if (converter) {
c = converter->approximateFromRenderedQColor(qcolor);
} else {
c = KoDumbColorDisplayRenderer::instance()->approximateFromRenderedQColor(qcolor);
}
} else {
c = KoDumbColorDisplayRenderer::instance()->approximateFromRenderedQColor(qcolor);
}
return new ManagedColor(c);
}
QString ManagedColor::colorDepth() const
{
return d->color.colorSpace()->colorDepthId().id();
......
......@@ -82,6 +82,15 @@ public:
* the correct configuration applied.
*/
QColor colorForCanvas(Canvas *canvas) const;
/**
* @brief fromQColor is the (approximate) reverse of colorForCanvas()
* @param qcolor the QColor to convert to a KoColor.
* @param canvas the canvas whose color management you'd like to use.
* @return the approximated ManagedColor, to use for canvas resources.
*/
static ManagedColor *fromQColor(const QColor &qcolor, Canvas *canvas = 0);
/**
* colorDepth A string describing the color depth of the image:
* <ul>
......
......@@ -8,6 +8,7 @@ public:
ManagedColor(const QString &colorModel, const QString &colorDepth, const QString &colorProfile, QObject *parent = 0);
bool operator==(const ManagedColor &other) const;
QColor colorForCanvas(Canvas *canvas) const;
static ManagedColor *fromQColor(const QColor &qcolor, Canvas *canvas = 0);
QString colorDepth() const;
QString colorModel() const;
QString colorProfile() const;
......
......@@ -94,6 +94,7 @@ install_pykrita_plugin(lastdocumentsdocker)
install_pykrita_plugin(comics_project_management_tools)
install_pykrita_plugin(krita_script_starter)
install_pykrita_plugin(plugin_importer)
install_pykrita_plugin(mixer_slider_docker)
# if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3)
# install_pykrita_plugin(cmake_utils)
......
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Krita-docker-color-slider Plugin Manual</title>
</head>
<body>
<h1 id="mixer-slider">Mixer Slider Docker</h1>
<p>A docker which allows you to choose from the gradient between two colors.</p>
<h2 id="basic-usage">Basic Usage</h2>
<p>Go to <strong>Settings -&gt; Configure Krita -&gt; Python Plugin Manager</strong>.
Enable "Mixer Slider docker."</p>
<p>Right-click on menu bar, and enable "Mixer Slider Docker." A docker will appear in the window.
The button on the left with the text "S" is for settings. Click on it to set the number of slider lines.</p>
<p>The right part of the docker contains the sliders. Each line has a left color, a slider bar, and a right color.</p>
<p>Click the left/right color to set it to the current foreground color. Click and move on the slider to set
the current foreground color to the color your mouse is hovering. An indicator will appear to show the color
you just selected.</p>
</body>
</html>
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from .mixer_slider_docker import *
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QPixmap, QPainter, QColor, QBrush, QPolygon
from PyQt5.QtCore import QPoint
from krita import ManagedColor
class ColorSlider(QWidget):
default_color = ManagedColor("", "", "")
def __init__(self, docker, left_color=default_color, right_color=default_color, parent=None):
super(ColorSlider, self).__init__(parent)
self.docker = docker
self.left_color = left_color
self.right_color = right_color
self.slider_pixmap = None
self.value_x = None
self.cursor_fill_color = QColor.fromRgbF(1, 1, 1, 1)
self.cursor_outline_color = QColor.fromRgbF(0, 0, 0, 1)
self.need_redraw = True
def set_color(self, pos, color):
if pos == 'left':
if self.left_color is not color:
self.left_color = color
self.need_redraw = True
else:
if self.right_color is not color:
self.right_color = color
self.need_redraw = True
def update_slider(self):
'''
Update the slider to a gradient between the two colors.
The painting of the slider comes from the program Krita. The original code can be accessed
at the following URL.
https://github.com/KDE/krita/blob/master/plugins/dockers/advancedcolorselector/kis_shade_selector_line.cpp
'''
if self.need_redraw:
patch_count = self.width()
base_hsva = list(self.docker.managedcolor_to_qcolor(self.left_color).getHsvF())
dest_hsva = list(self.docker.managedcolor_to_qcolor(self.right_color).getHsvF())
diff_hsva = [(dest_hsva[i] - base_hsva[i]) for i in range(4)]
if dest_hsva[0] == -1.0:
diff_hsva[0] = 0
elif base_hsva[0] == -1.0:
diff_hsva[0] = 0
base_hsva[0] = dest_hsva[0]
elif diff_hsva[0] > 0.5: # make sure the sliding goes through a minor arc
diff_hsva[0] = diff_hsva[0] - 1.0
elif diff_hsva[0] < -0.5:
diff_hsva[0] = diff_hsva[0] + 1.0
step_hsva = [x / patch_count for x in diff_hsva]
self.slider_pixmap = QPixmap(self.width(), self.height())
painter = QPainter(self.slider_pixmap)
for i in range(patch_count):
hue = base_hsva[0] + i * step_hsva[0]
while hue < 0.0:
hue += 1.0
while hue > 1.0:
hue -= 1.0
saturation = base_hsva[1] + i * step_hsva[1]
value = base_hsva[2] + i * step_hsva[2]
cur_color = QColor.fromHsvF(hue, saturation, value)
painter.fillRect(i, 0, 1, self.height(), cur_color)
painter.end()
self.need_redraw = False
widget_painter = QPainter(self)
self.rendered_image = self.slider_pixmap.toImage()
widget_painter.drawImage(0, 0, self.rendered_image)
if self.value_x is not None:
start_x = self.value_x
start_y = self.height() / 2
delta_x = self.height() / 3
delta_y = self.height() / 3
points = [QPoint(start_x, start_y),
QPoint(start_x - delta_x, start_y + delta_y),
QPoint(start_x + delta_x, start_y + delta_y)]
widget_painter.setBrush(QBrush(self.cursor_fill_color))
widget_painter.setPen(self.cursor_outline_color)
widget_painter.drawPolygon(QPolygon(points))
def paintEvent(self, event):
self.update_slider()
def resizeEvent(self, event): # after resizing the widget, force-redraw the underlying slider
self.need_redraw = True
def adjust_pos_x(self, x): # adjust the x to make it in the range of [0, width - 1]
if x < 0:
return 0
if x >= self.width():
return self.width() - 1
return x
def mouseMoveEvent(self, event):
pos = event.pos()
self.value_x = self.adjust_pos_x(pos.x())
self.update()
def mouseReleaseEvent(self, event):
pos = event.pos()
self.value_x = self.adjust_pos_x(pos.x())
y = int(self.height() / 2)
fixed_pos = QPoint(self.value_x, y)
color = self.rendered_image.pixelColor(fixed_pos)
mc = self.docker.qcolor_to_managedcolor(color)
if self.docker.canvas() is not None:
if self.docker.canvas().view() is not None:
self.docker.canvas().view().setForeGroundColor(mc)
self.update()
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=mixer_slider_docker
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=Mixer Slider docker
Comment=A color slider.
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton
from krita import Krita, DockWidget, ManagedColor, DockWidgetFactory, DockWidgetFactoryBase
from .slider_line import SliderLine
from .ui_mixer_slider_docker import UIMixerSliderDocker
class MixerSliderDocker(DockWidget):
# Init the docker
def __init__(self):
super(MixerSliderDocker, self).__init__()
main_program = Krita.instance()
settings = main_program.readSetting("", "MixerSliderColors",
"RGBA,U8,sRGB-elle-V2-srgbtrc.icc,1,0.8,0.4,1|" +
"RGBA,U8,sRGB-elle-V2-srgbtrc.icc,0,0,0,1") # alpha=1 == non-transparent
self.default_left_color = self.qcolor_to_managedcolor(QColor.fromRgbF(0.4, 0.8, 1, 1))
self.default_right_color = self.qcolor_to_managedcolor(QColor.fromRgbF(0, 0, 0, 1))
# make base-widget and layout
self.widget = QWidget()
self.sliders = []
self.top_layout = QVBoxLayout()
self.main_layout = QHBoxLayout()
self.top_layout.addLayout(self.main_layout)
self.top_layout.addStretch(0)
# The text on the button for settings
self.settings_button = QPushButton(i18n('S'))
self.settings_button.setToolTip(i18n('Change settings'))
self.settings_button.setMaximumSize(30, 30)
self.main_layout.addWidget(self.settings_button)
self.layout = QVBoxLayout()
self.layout.setSpacing(0)
self.main_layout.addLayout(self.layout)
for line in settings.split(";"):
colors = line.split('|')
if len(colors) < 2: # discard old configurations
continue
left_color = self.parse_color(colors[0].split(','))
right_color = self.parse_color(colors[1].split(','))
widget = SliderLine(left_color, right_color, self)
self.sliders.append(widget)
self.layout.addWidget(widget)
self.widget.setLayout(self.top_layout)
self.setWindowTitle(i18n("Mixer Slider Docker"))
self.setWidget(self.widget)
[x.show() for x in self.sliders]
self.settings_button.clicked.connect(self.init_ui)
def settings_changed(self):
if self.ui.line_edit is not None:
num_sliders = int(self.ui.line_edit.text())
if len(self.sliders) > num_sliders:
for extra_line in self.sliders[num_sliders:]:
self.layout.removeWidget(extra_line)
extra_line.setParent(None)
self.sliders = self.sliders[0:num_sliders]
elif len(self.sliders) < num_sliders:
for i in range(num_sliders - len(self.sliders)):
widget = SliderLine(self.default_left_color, self.default_right_color, self)
self.sliders.append(widget)
self.layout.addWidget(widget)
self.write_settings()
def get_color_space(self):
if self.canvas() is not None:
if self.canvas().view() is not None:
canvas_color = self.canvas().view().foregroundColor()
return ManagedColor(canvas_color.colorModel(), canvas_color.colorDepth(), canvas_color.colorProfile())
return ManagedColor('RGBA', 'U8', 'sRGB-elle-V2-srgbtrc.icc')
def init_ui(self):
self.ui = UIMixerSliderDocker()
self.ui.initialize(self)
def write_settings(self):
main_program = Krita.instance()
setting = ';'.join(
[self.color_to_settings(line.left) + '|' + self.color_to_settings(line.right)
for line in self.sliders])
main_program.writeSetting("", "MixerSliderColors", setting)
def color_to_settings(self, managedcolor):
return ','.join([managedcolor.colorModel(),
managedcolor.colorDepth(),
managedcolor.colorProfile()] +
[str(c) for c in managedcolor.components()])
def parse_color(self, array):
color = ManagedColor(array[0], array[1], array[2])
color.setComponents([float(x) for x in array[3:]])
return color
def canvasChanged(self, canvas):
pass
def qcolor_to_managedcolor(self, qcolor):
mc = ManagedColor.fromQColor(qcolor, self.canvas())
return mc
def managedcolor_to_qcolor(self, managedcolor):
return managedcolor.colorForCanvas(self.canvas())
Application.addDockWidgetFactory(DockWidgetFactory("mixer_slider_docker", DockWidgetFactoryBase.DockRight, MixerSliderDocker))
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from PyQt5.QtWidgets import QDialog
class SettingsDialog(QDialog):
def __init__(self, ui_mixer_slider, parent=None):
super(SettingsDialog, self).__init__(parent)
self.ui_mixer_slider = ui_mixer_slider
def accept(self):
self.ui_mixer_slider.docker.settings_changed()
super(SettingsDialog, self).accept()
def closeEvent(self, event):
event.accept()
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from PyQt5.QtWidgets import QHBoxLayout, QWidget
from PyQt5.QtGui import QPixmap, QPainter
from PyQt5.Qt import pyqtSlot, pyqtSignal
from .color_slider import ColorSlider
class SliderBtn(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super(SliderBtn, self).__init__(parent)
def set_color(self, qcolor):
self.color = qcolor
self.update()
def update_color(self):
color_sq = QPixmap(self.width(), self.height())
color_sq.fill(self.color)
image = color_sq.toImage()
painter = QPainter(self)
painter.drawImage(0, 0, image)
def paintEvent(self, event):
self.update_color()
def mouseReleaseEvent(self, event):
self.clicked.emit()
class SliderLine(QWidget):
def __init__(self, left_color, right_color, docker, parent=None):
super(SliderLine, self).__init__(parent)
self.left_button = SliderBtn()
self.right_button = SliderBtn()
self.docker = docker
self.color_slider = ColorSlider(docker)
self.layout = QHBoxLayout()
self.layout.setContentsMargins(2, 2, 2, 2)
self.setLayout(self.layout)
self.layout.addWidget(self.left_button)
self.layout.addWidget(self.color_slider)
self.layout.addWidget(self.right_button)
self.left_button.clicked.connect(self.slot_update_left_color)
self.right_button.clicked.connect(self.slot_update_right_color)
self.set_color('left', left_color)
self.set_color('right', right_color)
self.left_button.setMinimumSize(30, 30)
self.left_button.setMaximumSize(30, 30)
self.right_button.setMinimumSize(30, 30)
self.right_button.setMaximumSize(30, 30)
self.color_slider.setMaximumHeight(30)
def set_color(self, pos, color):
button_to_set = None
if pos == 'left':
self.left = color
button_to_set = self.left_button
else:
self.right = color
button_to_set = self.right_button
self.color_slider.set_color(pos, color)
button_to_set.set_color(self.docker.managedcolor_to_qcolor(color))
@pyqtSlot()
def slot_update_left_color(self):
if self.docker.canvas() is not None:
if self.docker.canvas().view() is not None:
self.set_color('left', self.docker.canvas().view().foregroundColor())
self.color_slider.value_x = 0 # set the cursor to the left-most
self.color_slider.update()
self.docker.write_settings()
@pyqtSlot()
def slot_update_right_color(self):
if self.docker.canvas() is not None:
if self.docker.canvas().view() is not None:
self.set_color('right', self.docker.canvas().view().foregroundColor())
self.color_slider.value_x = self.color_slider.width() - 1
self.color_slider.update()
self.docker.write_settings()
'''
Copyright (C) 2019 Tusooa Zhu <tusooa@vista.aero>
This file is part of Krita-docker-color-slider.
Krita-docker-color-slider is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Krita-docker-color-slider is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Krita-docker-color-slider. If not, see <https://www.gnu.org/licenses/>.
'''
from PyQt5.QtWidgets import QDialogButtonBox, QLabel, QVBoxLayout, QHBoxLayout, QSpinBox
from PyQt5.QtGui import QIntValidator
from PyQt5.QtCore import Qt
import krita
from .settings_dialog import SettingsDialog
class UIMixerSliderDocker(object):
def __init__(self):
self.krita_instance = krita.Krita.instance()
self.main_dialog = SettingsDialog(self, self.krita_instance.activeWindow().qwindow())
self.button_box = QDialogButtonBox(self.main_dialog)
self.vbox = QVBoxLayout(self.main_dialog)
self.hbox = QHBoxLayout(self.main_dialog)
self.line_edit = None
self.button_box.accepted.connect(self.main_dialog.accept)
self.button_box.rejected.connect(self.main_dialog.reject)
self.button_box.setOrientation(Qt.Horizontal)
self.button_box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
def initialize(self, docker):
self.docker = docker
self.vbox.addLayout(self.hbox)
self.hbox.addWidget(QLabel(i18n('Number of slider lines: ')))
self.line_edit = QSpinBox()
self.line_edit.setValue(len(docker.sliders))
self.hbox.addWidget(self.line_edit)
self.vbox.addWidget(self.button_box)
self.main_dialog.show()
self.main_dialog.activateWindow()
self.main_dialog.exec_()