Small selection buttons[plugin]

I made small plugin :smiling_face:
I’m sorry I didn’t put it together in a zip file.
and sorry for my bad English.

#smallselectiondocker.py

from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QWidget
from PyQt5.QtCore import Qt
from krita import Selection, DockWidget

DOCKER_TITLE = 'SmallSelection buttons'
DOCKER_ID = 'small_selection_buttons'

class DockerButton(QPushButton):
    def __init__(self, btn_name=None, icon_text=None, btn_tooltip=None):
        super(DockerButton, self).__init__()
        self.setText(btn_name)
        self.setIcon(Krita.instance().icon(icon_text))
        self.setToolTip(btn_tooltip)
        self.setFixedHeight(24)


class SmallSelectionButtonsDocker(DockWidget):
    def __init__(self):
        super(SmallSelectionButtonsDocker, self).__init__()
        self.setWindowTitle(DOCKER_TITLE)
        self.setFixedSize(200, 100)
        wdget = QWidget(self)
        vbox = QVBoxLayout()
        vbox.setContentsMargins(2, 0, 2, 0)
        vbox.setSpacing(0)
        wdget.setLayout(vbox)
        
        hbox1 = QHBoxLayout()
        hbox2 = QHBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)
        # ~ vbox.addLayout(hbox3)
        
        x_btn = DockerButton('x', '', '')
        x_btn.clicked.connect(lambda: self.selectDirection('x'))
        y_btn = DockerButton('y', '', '')
        y_btn.clicked.connect(lambda: self.selectDirection('y'))
        left_btn = DockerButton('', 'arrow-left', 'left')
        left_btn.clicked.connect(lambda: self.selectDirection('left'))
        right_btn = DockerButton('', 'arrow-right', 'right')
        right_btn.clicked.connect(lambda: self.selectDirection('right'))
        top_btn = DockerButton('', 'arrow-up', 'top')
        top_btn.clicked.connect(lambda: self.selectDirection('top'))
        bottom_btn = DockerButton('', 'arrow-down', 'bottom')
        bottom_btn.clicked.connect(lambda: self.selectDirection('bottom'))
        
        hbox1.addWidget(x_btn)
        hbox1.addWidget(y_btn)
        hbox1.addWidget(left_btn)
        hbox1.addWidget(right_btn)
        hbox1.addWidget(top_btn)
        hbox1.addWidget(bottom_btn)

        ref_btn = DockerButton('', 'config-canvas-only', 'convert selection to referenceImage')
        ref_btn.clicked.connect(self.on_ref_btn_clicked)
        clear_invertedArea_btn = DockerButton('', 'edit-clear-16', 'clear invertedArea')
        clear_invertedArea_btn.clicked.connect(self.clear_invertedArea)
        samplerA_btn = DockerButton('', 'krita_tool_color_sampler', 'sample screen color')
        samplerA_btn.clicked.connect(self.sample_screenColor)
        paste_selection_btn = DockerButton('', 'edit-paste', 'paste selection into same layer')
        paste_selection_btn.clicked.connect(self.pasteIntoSameLayer)
        
        hbox2.addWidget(ref_btn)
        hbox2.addWidget(clear_invertedArea_btn)
        hbox2.addWidget(samplerA_btn)
        hbox2.addWidget(paste_selection_btn)
        
        self.setWidget(wdget)
        self.setAllowedAreas(Qt.NoDockWidgetArea)
        self.setFloating(True)



    def selectDirection(self, direction:str):
        doc = Krita.instance().activeDocument()
        get_selection = doc.selection()
        if not get_selection:
            return
        new_selection = Selection()
        if direction == 'x':
            new_selection.select(doc.bounds().x(), get_selection.y(), doc.width(), get_selection.height(), 255)
        elif direction == 'y':
            new_selection.select(get_selection.x(), doc.bounds().y(), get_selection.width(), doc.height(), 255)
        elif direction == 'left':
            new_selection.select(doc.bounds().x(), get_selection.y(), get_selection.x()+get_selection.width(), get_selection.height(), 255)
        elif direction == 'right':
            new_selection.select(get_selection.x(), get_selection.y(), doc.width()-get_selection.x(), get_selection.height(), 255)
        elif direction == 'top':
            new_selection.select(get_selection.x(), doc.bounds().y(), get_selection.width(), get_selection.y()+get_selection.height(), 255)
        elif direction == 'bottom':
            new_selection.select(get_selection.x(), get_selection.y(), get_selection.width(), doc.height()-get_selection.y(), 255)
        doc.setSelection(new_selection)


    def on_ref_btn_clicked(self):
        doc = Krita.instance().activeDocument()
        if not doc.selection():
            Krita.instance().action('select_all').trigger()
        Krita.instance().action('edit_copy').trigger()
        Krita.instance().action('paste_as_reference').trigger()
        doc.waitForDone()
        Krita.instance().action('deselect').trigger()


    def clear_invertedArea(self):
        doc = Krita.instance().activeDocument()
        currentLayer = doc.activeNode()
        s = doc.selection()
        all_s = Selection()
        all_s.select(0,0, doc.width(), doc.height(), 255)
        all_s.symmetricdifference(s)
        doc.setSelection(all_s)
        doc.waitForDone()
        Krita.instance().action('clear').trigger()
        doc.refreshProjection()


    def sample_screenColor(self):
        qwin = Krita.instance().activeWindow().qwindow()
        pobj = qwin.findChild(QWidget,'screenColorSamplerWidget')
        wobj = pobj.findChild(QPushButton)
        wobj.click()


    def pasteIntoSameLayer(self):
        doc = Krita.instance().activeDocument()
        s = doc.selection()
        if s:
            Krita.instance().action('edit_copy').trigger()
            Krita.instance().action('paste_into').trigger()
        else:
            Krita.instance().action('selectopaque_add').trigger()
            doc.waitForDone()
            Krita.instance().action('edit_copy').trigger()
            Krita.instance().action('paste_into').trigger()


    def canvasChanged(self, canvas):
        pass

#init.py

from krita import DockWidgetFactory, DockWidgetFactoryBase
from .smallselectiondocker import SmallSelectionButtonsDocker

DOCKER_ID = 'small_selection_buttons'


dock_widget_factory = DockWidgetFactory(DOCKER_ID,
                        DockWidgetFactoryBase.DockRight,
                        SmallSelectionButtonsDocker)

Krita.instance().addDockWidgetFactory(dock_widget_factory)

#smallselectiondocker.desktop

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=smallselectionDocker
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=small_selection_docker
Comment=Getting a docker set up

num_001

11 Likes

Best to put it up on GitHub for everyone to easily find and download.

1 Like

Thanks for the comment. I don’t know much about how to use github. I’ll try it when I have time :smiling_face:

It would be interesting if docker could be made a bit more compact like this:

And could you add a button to invert the selection? It would be very helpful :blush:

Thank you very much for the plugin.

@pntar567, @all:
I have a little problem with the plugin. The error message in the “Python Plugin Manager” reads:
“Module not loaded:
ModuleNotFoundError: No module named ‘smallselectionDocker’.
Could not import smallselectionDocker”

Since I created the three files “smallselectiondocker.desktop, smallselectiondocker.py, init.py”, by copy and paste, I can’t have copied anything wrong and therefore suspect a small typo in one of the three files, only my limited knowledge didn’t allow me to detect even any error.
So far, with three installations of Krita 5.0.6, differently equipped with plugins and the 5.1.0-prealpha-a71bfe56c5 on Windows 10, I have not been able to get it to work. I even created the init.py as init.py on a test basis, since this is how it is done with other plugins, but this too without success. What am I missing or what am I doing wrong.

Michelist

I did a small tweak on the desktop and init files I did not alter the main code and now it boots.
I will share the files for a couple of days. @Michelist
((dead link))

2 Likes

Sorry for the late reply.
@Michelist
I uploaded my code on github after my previous post. please try it If you fail to install the plugin.

@EyeOdin
I have little coding experience. Thank you for your help.
@SchrodingerCat
I don’t know if my code is correct, but I think it will be like a toolbar if you change the code as following.

from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QWidget, QDockWidget
from PyQt5.QtCore import Qt
from krita import Selection, DockWidget

#DOCKER_TITLE is long, so I shortened it
DOCKER_TITLE = 'ssbtn'
DOCKER_ID = 'small_selection_buttons'

class DockerButton(QPushButton):
    def __init__(self, btn_name='', icon_text='', btn_tooltip=''):
        super(DockerButton, self).__init__()
        self.setText(btn_name)
        self.setIcon(Krita.instance().icon(icon_text))
        self.setToolTip(btn_tooltip)
        self.setFixedSize(28, 24)


class SmallSelectionButtonsDocker(DockWidget):
    def __init__(self):
        super(SmallSelectionButtonsDocker, self).__init__()

        self.setWindowTitle(DOCKER_TITLE)
        self.setFeatures(QDockWidget.DockWidgetVerticalTitleBar | QDockWidget.DockWidgetFloatable|QDockWidget.DockWidgetMovable)
        
        wdget = QWidget(self)
        wdget.setFixedSize(310, 26)
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(0)
        wdget.setLayout(hbox)

        x_btn = DockerButton('', 'zoom-horizontal', 'x')
        x_btn.clicked.connect(lambda: self.selectDirection('x'))
        y_btn = DockerButton('', 'zoom-vertical', 'y')
        y_btn.clicked.connect(lambda: self.selectDirection('y'))
        left_btn = DockerButton('', 'arrow-left', 'left')
        left_btn.clicked.connect(lambda: self.selectDirection('left'))
        right_btn = DockerButton('', 'arrow-right', 'right')
        right_btn.clicked.connect(lambda: self.selectDirection('right'))
        top_btn = DockerButton('', 'arrow-up', 'top')
        top_btn.clicked.connect(lambda: self.selectDirection('top'))
        bottom_btn = DockerButton('', 'arrow-down', 'bottom')
        bottom_btn.clicked.connect(lambda: self.selectDirection('bottom'))
        ref_btn = DockerButton('', 'config-canvas-only', 'convert selection to referenceImage')
        ref_btn.clicked.connect(self.on_ref_btn_clicked)
        clear_invertedArea_btn = DockerButton('', 'edit-clear-16', 'clear invertedArea')
        clear_invertedArea_btn.clicked.connect(self.clear_invertedArea)
        samplerA_btn = DockerButton('', 'krita_tool_color_sampler', 'sample screen color')
        samplerA_btn.clicked.connect(self.sample_screenColor)
        paste_selection_btn = DockerButton('', 'edit-paste', 'paste selection into same layer')
        paste_selection_btn.clicked.connect(self.pasteIntoSameLayer)
        invert_btn = DockerButton('', 'filter-invert', 'invert selection')
        invert_btn.clicked.connect(lambda: Krita.instance().action('invert_selection').trigger())

        button_list = [x_btn, y_btn, left_btn, right_btn, top_btn, bottom_btn, ref_btn, clear_invertedArea_btn,
                    samplerA_btn, paste_selection_btn, invert_btn]
        
        for button in button_list:
            hbox.addWidget(button)

        self.setWidget(wdget)
        self.setAllowedAreas(Qt.NoDockWidgetArea)
        self.setFloating(True)

  #(The following text is omitted)

:smiling_face:

2 Likes

Thanks for your update and for the plugin itself!
:+1:

Actually, I have no problem with given program texts to “convert” into appropriate files and place them in the appropriate folder structure of Krita, I have not done it for the first time. I had even packed the three files, with matching folder structure, into a ZIP file, so that I could install it, only my Krita didn’t “like” it.
Well, it’s no big deal, I think you will have looked at @EyeOdin’s customization, which is now also accepted by my PC.
It wasn’t much, it’s just that with my (non-existent) knowledge of Python, I wasn’t able to work out the customizations EyeOdin made. Just copy and paste happened to be insufficient this time, and without those customizations it wouldn’t work on my Windows PC, but this forum is a working community, so someone was able to help here too, EyeOdin.
I will also try your customization when I have time. :wink:

Michelist

No worries. When I came to the forum I just knew if’s and little else. Python was unknown to me and with some help around I was able to get a bit better. I am sure you will improve too.

The things I corrected the most was the references to file names and functions as there was many versions inside: smallselectiondocker , smallselectionDocker and small_selection_docker were some of the variants if I recall right.

Having a git page really helps as most people will not even know the correct folder to paste things inside. Using the python script installer with a zip will help a lot of people and still some people will be confused but is hard to help everyone.

I read your known issues. To fix the no document active error you need to add this if to the function that is triggered by the signal.

if ((self.canvas() is not None) and (self.canvas().view() is not None):

Also if you got a GitHub now I can close my temp link sooner.

1 Like