plugin communication?

Can two plugins send or read information between each other? I wonder this in order not too duplicate a bunch of code.

The thought just crossed my head. You import the other plugin as a module? Is that a thing? I need to test it.

Hi

yes a module imported by a plugin X is available for plugin Y

I currently have the problem of 2 plugins for which there’s a module with the same name and then, there’s a plugin cor which module is not as expected because already loaded by another one…

just have to think : you don’t know in which order plugins will be loaded, neither if both plugins will be available for user…

Grum999

1 Like

Oh that is a curious situation then. I wonder I placing the import in the startup Def would not solve that then.

The startup is the last thing to run after all the inits. It is how I solve all the formatting issues I had with pigmento while loading with the scaling menus that still don’t know they do stuff.

But between plugins I really donno I am still not at the computer to test it out.

@Grum999
Doing a import on the def showEvent(self, event): as I was thinking does not work. PyQt did not like it and even told me otherwise hehehe.

I rewrote a quick extension for pigmento to try and connect them by sending a signals from the extension shortcut action into the docker and trigger one of its functions inside, however something is not allowing the connection to be made. I took a screenshot of the important stuff I added to connect them.

Krita responds to the Extension inputs but Pigmentostays silent on the initialize of the Extension lines to connect the signal. is this the wrong approach or some odd typo?

Hi

There’s a wrong approach I think.

I don’t have all the code under my eye, but I suppose that pigment_o_extension.py is initialised at startup?
If yes, In pigment_o_docker.py in Extension method, line 1642, you’re creating a new instance of extension, and it’s not the same than the one created at startup.

Here a working example with 2 differents plugins (i’m not sure if you have 2 plugins intalled or if docker and extension are installed from the same plugin… )

It’s one possible implementation, I choose the simplest for me :slight_smile:

Plugin testA (create action, emit signal)

testa/testa.py

from PyQt5.Qt import *
from PyQt5.QtCore import pyqtSignal as Signal
from krita import *

EXTENSION_ID = 'pykrita_testA'

class TestA(Extension):
    colorChanged = Signal(QColor)

    def __init__(self, parent):
        super().__init__(parent)
        
        # store current instance of extension as a property of krita instance
        Krita.instance().setProperty('testA', self)

    def setup(self):
        pass

    def createActions(self, window):
        # create actions
        actionRed = window.createAction(EXTENSION_ID+'red', "Test color: Red", "tools/scripts")
        actionRed.triggered.connect(self.actionRed)        
        actionGreen = window.createAction(EXTENSION_ID+'green', "Test color: Green", "tools/scripts")
        actionGreen.triggered.connect(self.actionGreen)        
        actionBlue = window.createAction(EXTENSION_ID+'blue', "Test color: Blue", "tools/scripts")
        actionBlue.triggered.connect(self.actionBlue)        

    def actionRed(self):
        # emit signal for red color
        self.colorChanged.emit(QColor(Qt.red))

    def actionGreen(self):
        # emit signal for green color
        self.colorChanged.emit(QColor(Qt.green))

    def actionBlue(self):
        # emit signal for blue color
        self.colorChanged.emit(QColor(Qt.blue))

testa/__init__.py

from .testa import TestA

# And add the extension to Krita's list of extensions:
app = Krita.instance()
# Instantiate your class:
extension = TestA(parent=app)
app.addExtension(extension)

testa.desktop

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=testa
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=TestA
Comment=TestA plugin
Plugin testD (docker, receive signal from plugin testA)
from PyQt5.Qt import *
from krita import *


EXTENSION_ID = 'pykrita_testd'
MENU_ENTRY = 'TestD script'
  

class WColoredBox(QWidget):
    """A simple widget to display color"""
    def __init__(self, parent=None):
        super(WColoredBox, self).__init__(parent)
        
        self.__color=QColor(Qt.black)
        self.setMinimumSize(100,100)
        
    def paintEvent(self, event):
        canvas=QPainter(self)
        canvas.fillRect(event.rect(), QBrush(self.__color))
        
    def setColor(self, color):
        if isinstance(color, QColor):
            self.__color=color
            self.update()
            
    def color(self):
        return self.__color
  

class WTestD(QWidget):
    """Docker UI content"""
    def __init__(self, parent=None):
        super(WTestD, self).__init__(parent)
        
        self.__initialised=False
        
        # initialise ui
        layout=QVBoxLayout(self)
        self.__lbl=QLabel('Black')
        self.__lbl.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
        self.__color=WColoredBox(self)
        self.__connectedTestAExtension=None
        
        layout.addWidget(self.__lbl)
        layout.addWidget(self.__color)
        
        # if krita 5.0.0, this is the best method
        #self.__notifier=Krita.instance().notifier()
        #self.__notifier.windowCreated.connect(self.__initialise)
        
    def __initialise(self):
        if self.__initialised:
            # do not apply initialisation more than one time
            return 
        
        # retrieve instance of plugin testA
        self.__connectedTestAExtension=Krita.instance().property('testA')
        if not self.__connectedTestAExtension is None:
            # instance found (plugin installed and initialised)
            self.__connectedTestAExtension.colorChanged.connect(self.__actionFromTestA)
            # ensure to do this action only one time
            self.__initialised=True
        
    def __actionFromTestA(self, color):
        # set color provided from extension plugin
        self.__lbl.setText(color.name())
        self.__color.setColor(color)


    def initialise(self):
        self.__initialise()

class TestD(DockWidget):
    """Class to manage current selection"""

    def __init__(self):
        super(TestD, self).__init__()
        self.setWindowTitle('TestD plugin')
        self.__ui=WTestD()
        self.setWidget(self.__ui)

    def canvasChanged(self, canvas):
        """notifies when views are added or removed"""
        # if krita 4, windowCreated() signal doesn't work and then, dirty method..
        self.__ui.initialise()

testd/__init__.py

from krita import DockWidgetFactory, DockWidgetFactoryBase
from .testd import *

instance = Krita.instance()

testDocker = DockWidgetFactory(f'testd_test_docker',
                                    DockWidgetFactoryBase.DockRight,
                                    TestD)

instance.addDockWidgetFactory(testDocker)

testd.desktop

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=testd
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=TestD
Comment=TestD plugin

Result example:

Grum999

1 Like

Hi

I just think about this possibility:
Krita.instance().extensions()

Krita already maintains a list of extensions then, the setProperty() and property() usage can be replaced by a search in the returned list

Grum999

# my_extension_a/extension.py

from krita import (
    Krita,
    Extension)

class MyExtensionA(Extension):
    def find_my_buddy(self):
        from my_extension_b.extension import MyExtensionB
        app = Krita.instance()
        buddy = next((e for e in app.extensions() if isinstance(e, MyExtensionB)), None)
        print(buddy)
# my_extension_b/extension.py

from krita import (
    Krita,
    Extension)

class MyExtensionB(Extension):
    def find_my_buddy(self):
        from my_extension_a.extension import MyExtensionA
        app = Krita.instance()
        buddy = next((e for e in app.extensions() if isinstance(e, MyExtensionA)), None)
        print(buddy)

This should work. (not tested)

Note that if my_extension_x/__init__.py module is imported ie. import my_extension_x, then any plugin registering code would run, so it’s better to keep class MyExtensionX(Extension) in separate file, so it can be imported, without triggering registering.

/Aki

it is accepting it look :scream_cat: :exploding_head:
I am using shortcut keys to change the values here, but I am gonna try build up a bit more on top of it.
There are things accessible to Extensions that are not too Dockers by default so I will be able to some new tricks like this.

The way I have it set up is to have one init to kick start both Docker and Extension as if they were two different plugins.

init.py

# Imports
from krita import *
from PyQt5 import *
from .pigment_o_docker import *
from .pigment_o_extension import *

# Name the Python Script for the program
DOCKER_ID = "pykrita_pigment_o_docker"

# Register Krita Docker
Application.addDockWidgetFactory( DockWidgetFactory(DOCKER_ID, DockWidgetFactoryBase.DockRight, PigmentODocker) )
# Register Krita Extension
Krita.instance().addExtension(PigmentOExtension(Krita.instance()))

Having them talk to each other I can open more doors, one that was closed was action shortcuts that are better than key events by far.

In this case things could be simplified; You can register extension before Docker and then when docker is created you can do connection immediately, without having to use my trick to initialize connection through windowCreated() signal or canvasChanged() event.

Grum999