How to do key press and key release in script please?

Please, I’d like to set position_lock = True on trigger (key down) and when the key is released I’d like to turn it back to False but I have no idea how to handle the key press. Right now it works only once (since triggers goes off but I don’t have anything to handle the release).

I’m also not familiar with Python at all so the code is probably strange (I don’t think I’m using the self keyword properly) sry for that. I tried defining a keyReleaseEvent but honestly I have no idea how to do it to make it work.

I’d really appreciate your advice. Thank you very much.

So far I have this

from krita import *
from PyQt5.QtWidgets import *
import math

class CustomCanvasRotationExtension(Extension):
  def __init__(self,parent):
    super().__init__(parent)

  def createActions(self,window):
    self.c_rotation = window.createAction("c_rotation", "Custom Canvas Rotation", "tools/scripts")
    self.c_rotation.setAutoRepeat(True)

    self.position = QCursor.pos()
    self.position_lock = False
    self.safe_buffer = 30
    self.iii = 0

    # krita = Krita.instance()
    # self.canvas = Krita.instance().activeWindow().activeView().canvas()

    @self.c_rotation.triggered.connect
    def on_trigger():
      canvas = Krita.instance().activeWindow().activeView().canvas()
      # self.canvas doesn't work, no idew why, need to check what self actually means

      if not self.position_lock:
        self.position = QCursor.pos()
        self.position_lock = True
      
      distance = math.sqrt( math.pow((QCursor.pos().x() - self.position.x()), 2) + math.pow((QCursor.pos().y() - self.position.y()), 2)  )

      if distance > self.safe_buffer:
        canvas.setRotation(self.iii)
        self.iii += 3
      # canvas.resetRotation()

  def setup(self):
    pass

Krita.instance().addExtension(CustomCanvasRotationExtension(Krita.instance()))

You can modify my addon, that does that, but for switching between various tools.

It uses something called event filter, that is installed on the Qt’s mdiArea (basically the canvas in tabs mode, and place where subwindows are displayed).
It catches all the events going on, and you can do some stuff on events, that come from key pressing and releasing (two different events for that).

What’s worth noting, is that those events don’t occur, if a shortcut is set to that key, and events are being sent multiple times for each press and release (I solved that by counting time elapsed from previous event like that, and using only those that happened long after the other, but for simple use like yours without any toggling, maybe it won’t be necessary).

2 Likes

@wojtryb Thank you very much. I managed to get the key release to work in general but so far can’t figure out the shortcuts, is it really impossible to use the shortcut set for the action from krita’s settings?

On another note, would please know if there’s a way to print text out from plugin? I know from scripter I can use print but that doesn’t seem to work with plugins unfortunately.

Thank you very much!

As far as I know, you can’t use krita shortcuts, as then these events are not possible to catch. Maybe it is possible to use canvas inputs (modifiers), but I don’t know how to create these in a plugin.

To print the output from the addon I run the appimage from a linux terminal, and all the print() outputs land there. No idea how to do so on windows.

1 Like

Well to create a shortcut you will need to create a action file and make the entries there so krita knows what your talking about in the code.

The curious thing is that the actions only seem to connect with extensions objects and not docker objects. Or at least I was only able to connect it to extensions only.

In windows our terminal does not react to krita’s prints statements. What I tend to use (because I am making dockers) is a label and output my text into it. It is sucky but at least you see something. But since your with a extension that is not a option I think. Maybe the scripter output?

1 Like

I am not the best to explain python but…

Self is a redundant tag to explicitly say your talking about the class and everything inside where your making the call in and not something outside it. And things need that mark so they can be called by that reference and not a full path like from outside?

1 Like

@EyeOdin
Yeah I get the self tag now but since my python knowledge is basically zero it seems to work a bit differently than what I’m used with this keyword from other languages.

For example if I assign self.canvas = … (well canvas) to it it doesn’t work but if I assing it my variably such as self.whatever = “jeje” then this works so I was confused why this happens, I wanted to avoid to create canvas var every funciton call since it’s a non-changing var thus I’d prefer to keep it just once.
(I’ll need to figure this out later).

I also have the .action file for the shortcut I just don’t know how to use it in the code:

if e.type() == QEvent.KeyRelease and e.key() == shortcut:

This is basically what I’d like to do but I have no idea how to get the shortcut (since it can be assigned inside krita so I can’t do it manually - well I can do it the same way as @wojtryb does it which I think for sanity of my mind I’ll eventually do since I can’t figure it out).
From QAction I can get the shortcut with QAction.shortcut() but I have no idea what this gives or me if it’s even used inside Krita so I don’t know how to use it and without any print option right now I can’t even check xD
(I really need to figure out how to print something out of a plugin, I might use the option of creating a docker and printing it out there as you mentioned that was the only option I could figure out right now, or is there a way to to get to scripter through code maybe?)

@wojtryb Thanks a lot! :slight_smile: I’m just dumb and this is proving to be a challenge as expected though for such a small functionality gain that I’m starting to doubt myself xD

well my shortcut action progress is still very small but I think you can copy paste it to change it to your needs quickly.

on the action file in the action folder you can add this (mine is pigment_o.action):

<?xml version="1.0" encoding="UTF-8"?>
<ActionCollection version="2" name="Scripts">
    <Actions category="Scripts">
        <text>Pigment.O</text>

        <Action name="channels_rgb_red_minus">
          <text>RED Minus</text>
          <shortcut>none</shortcut>
          <isCheckable>false</isCheckable>
        </Action>

        <Action name="channels_rgb_red_plus">
          <text>RED Plus</text>
          <shortcut>none</shortcut>
          <isCheckable>false</isCheckable>
        </Action>

    </Actions>
</ActionCollection>

This creates 2 shorcuts for Krita
then you need to create action on the extension file in the pykrita file (mine is pigment_o.py):

class PigmentOExtension(Extension):

    def __init__(self, parent):
        super(PigmentOExtension, self).__init__(parent)

    def setup(self):
        self.value = 5

    def createActions(self, window):
        # RGB
        action_rgb_1_minus = window.createAction("rgb_red_plus", "RGB Red Minus")
        action_rgb_1_minus.triggered.connect(self.RGB_1_Minus)
        action_rgb_1_plus = window.createAction("rgb_red_plus", "RGB Red Plus")
        action_rgb_1_plus.triggered.connect(self.RGB_1_Plus)

    # RGB
    def RGB_1_Minus(self):
        pass
    def RGB_1_Plus(self):
        pass

My only issue is that all function operations I have are inside the docker class and I cant seem to call them I think I need to recreate them in the extension class as duplicates nor can I create actions inside the docker class it seems. but in your case you can call them naturally as all is inside the same space so you just need to fill out the functions that it connects too.

The only thing I am thoughtfull of if i make shortcuts is not not placing default values on them for the version I will distribute to other people. One they might have a krita default shortcuts layout or not and the keys you place might be already taken so I was thinking in just sending them blank and let Krita assign it after localy. However I still need to wrap my head around this better.

Anyways I hope it helps.

@EyeOdin
I think there was a little misunderstanding from the code I posted above I’m using it inside an eventFilter.

As I mentioned do have an .action file which allows me to assign it a shortcut this shortcut works with trigger but after that I need this shortcut for release event and I don’t know how (the one line code in my previous answer is related to this).

I need to do something on trigger (repeated) and on release. I have the part of trigger down just fine I don’t have the release fine (I can deal with key release in general it works but I can’t figure out how to let’s say if my action has a shortcut of “ctrl+shift+alt+x” assigned in Krita, how do I get this shortcut from code and how do I make the if statement with it for the release event (the one line I showed previously)?

This is where I’m currently lost :?

@wojtryb
I think you could maybe use event.modifiers() for modifiers but I’m still not sure how exactly (I really desperately need to figure out that printing in windows to see what I get from these calls).

what?..

you want to create a event filter instead of a actual shortcut on the system? you know that will cause keyboard assignment conflict to people if they are not alerted to it. Krita will execute both shortcut commands and then something weird happens I guess, so you need to mind that.

well what you want is on the event class thingy. if you already have the key press working your event filter is working good too. what you need is something like this:

    self.installEventFilter(self)
def eventFilter(self, source, event):
    if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_A):
        self.layout.label.setText("Pressed A")
    if (event.type() == QEvent.KeyRelease and event.key() == Qt.Key_A):
        self.layout.label.setText("Release A")
    return super(BlankDocker, self).eventFilter(source, event)

Other Methods:

def keyPressEvent(self, event):
    pass
def keyReleaseEvent(self, event):
    pass

@EyeOdin
Well I need a release key and I don’t know any other way to get to it but eventFilter, is there something else that I could use?

So the keys have to be hardcoded for the event.key() == ... I can take the krita shortcut for my action and use that here in some way?

well the Event filter and the system functions that have event inputs, both are shown above.

YUP Hard coded!
What you can do to ease that is just do a variable shortcut = Qt.Key_A and use that variable inside the code and place it like a header and then you can edit the shortcuts at the start. Or inside a class self.shortcut = Qt.Key_A during the init phase.
the alternative to do that better is the actions I spoke before but that does not have the release so your stuck with the ones with events.

P.s.
oh now I noticed I sent duplicated code sorry about that. It is all in Wojtryb code on the link he gave.

I know one can get modifiers pressed while the event happens by event.modifiers(). It’s handy when you want to set shortcuts with them. I meant krita canvas inputs like the line tool with V or layer selection with R - I don’t know how to create these and if it’s even possible in python, but that’s something that would let you to really set those within krita.

Otherwise these will have to be hardcoded in the addon unless you create a custom docker/modal window for setting them.

I see. Honestly I wasn’t sure what exactly you meant ;0 Thanks for letting me know.

At this point my plugin turned to be not working for some reason. I removed the action file since I don’t want it to have a shortcut assignable and it’s showed greyed out in plugins in krita’s settings.

I’m starting to go nuts from this. I wonder if there’s some cache I need to delete to reset the plugin for krita maybe?
:sob:

Grayed out name means the plugin haven’t compiled at all. If you hover over it, you can get the traceback error that caused it.

Thanks, it started working again. No changes whatsoever I have no idea what’s going on at this point.

Thanks a lot, I’ll be honestly copying parts of your code now ;0. I’ll credit your project somewhere when I get to it.

No need to credit as it’s all GPL. My code is also heavily based on redesign plugin and uses functions by akir. Feel free to copy whole chunks of my code.

Sadly this seems to be the only way to get this release button event. Hope it’ll work at the end. If you run into more trouble or need to get some more info on my code (still I haven’t made any comments there), just ask me.

well the modifier keys are:

event.modifiers() == QtCore.Qt.ShiftModifier
event.modifiers() == QtCore.Qt.ControlModifier
event.modifiers() == QtCore.Qt.AltModifier

I use them alot on mine and they make the docker not need a ton of extra buttons.

strangely enough I suggest you making a blank project to debug weird things. it is easyer to boot a small project and see what is wrong with it (having the new stuff your testing out) than having a bunch of stuff having crazy dependencies. I have a little docker that has nothing on it and I cant say how many times it has save me. I even have it on my github XD it is that good to have on your back pocket.

@wojtryb @EyeOdin

I’m still having a bit of a problem if someone please would know a way around it please.

I have:

def createActions(self, window):
    SHORTCUTS = [
      Qt.Key_Control,
      Qt.Key_Shift,
      Qt.Key_Alt,
      Qt.Key_X
    ]

    self.c_canvas_rotation = window.createAction("c_canvas_rotation", "Custom Canvas Rotation", "tools/scripts")
    self.c_canvas_rotation.setAutoRepeat(True)
    self.qwin = window.qwindow()
    self.winFilter = self.windowFilter(SHORTCUTS,
    [False] * len(SHORTCUTS),
    False)
    self.qwin.installEventFilter(self.winFilter)

Which install the EventFilter to a qwindow.

Which works ok, then I go to the EventFilter:

  class windowFilter(QWindow):
    def __init__(self, shortcuts, shortcuts_registered, shortcut_bool, parent=None):
      super().__init__(parent)
      self.SHORTCUTS = shortcuts
      self.SHORTCUTS_REGISTERED = shortcuts_registered
      self.SHORTCUT_BOOL = shortcut_bool

    def eventFilter(self, obj, event):
      # KEY PRESS
      if event.type() == QEvent.KeyPress:
        # Register key presses equal to SHORTCUTS
        for i in range(len(self.SHORTCUTS)):
          if event.key() == self.SHORTCUTS[i]:
            self.SHORTCUTS_REGISTERED[i] = True
        dialog = Dialog(self.SHORTCUTS_REGISTERED) # Dialog is just a way for me to print nothing else and works jsut fine

The problem is that KeyPress doesn’t work. Or to be precise it does kind of work if I use ALT key (this I assume works in windows as usual which means to give focus to menu and not the rest of the app) and then I can press alt, shift or ctrl and it works but it doesn’t work for other keys or most of them (if they are assigned to do something in krita it’s not working).

So 2 problems. It seems krita is catching keypresses on qwindow I guess and it doesn’t propagate to my code unless I change focus (which is not a working solution).

  1. Is there a way to get the keypresses here please?
  2. Is there a way to get kepyresses from keys that are already assigned (for example in my case I use ctrl, alt, shift, X as a combination just to see what I can use and X doesn’t work even out of focus (through alt)

Later I have a KeyRelease part but that one seems to be working just fine so I omitted it in this post.

The plugin itself is supposed to just do regular canvas rotation nothing more, I’m working on this: