Running a Loop in background Along with normal Krita Session

Hi,
I’m trying to run a loop in the background such that, the loop can do its job while I draw and use other functions simultaneously.

What is happening right now, is, as long as the loop is running, krita freezes, I tried using asyncio, but nothing changed

here is my code (ignore the InfoObject and instance variable for now, instance is just to keep the loop from going on forever)
any help?

Hi,

You can’t do it like this in a Qt application, you have to use Qt mechanism.

There’s different methods to execute a loop in background.

The simplest one is to use a processEvent
For performances, I do not recommend to use it :slight_smile:

from krita import *
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import (QEventLoop, QTimer)


def sleep(value):
    """Do a sleep of `value` milliseconds"""
    loop = QEventLoop()
    QTimer.singleShot(value, loop.quit)
    loop.exec()

i = 0
while(True):
    i+=1
    print(i)
    sleep(125)
    QApplication.instance().processEvents()

A more complex but better solution for performances might be to use a QRunnable, but, in fact, it will depends of what you really want to do.
What is the goal of having something running in the background?

Oh, another thing, rather than providing a screenshot of your code, just provide the code directly :slight_smile:
It’ll be easier to read, to test, to reproduce and provide a solution… :wink:

Grum999

Thnx for the help :blush:

and sorry for the screenshot, i assumed that its a small code, so wouldnt matter.
I’m not really a programmer so lel.

Im just trying to make a simple plugin that will allow me to make a timelapse without recording the entire screen, i just want to export the canvas on regular intervals of time.

I know its not performant, but given my skills with code and lack of any plugin, i thought of just exporting on intervals

Even for a small code, it’s easier to copy/paste than re-writting it :wink:

Ok I understand.
Note that export process can freeze Krita interface for a second (or more) according to canvas size, and drawing may not be ‘fluid’ anymore in this case.

Grum999

Exporting the whole image to the compressed file may be quite a performance heavy thing. Before you start, you can check out this trick that allows to prepare krita for recording with external program (basically creating new krita window with fullscreen canvas that isn’t moved or rotated while you draw).
There is also a discussion there about this WIP plugin for recording inside krita. Maybe this code could be useful to you, or you would like to help there instead of doing it all on your own :slight_smile:

That is already being worked on here :slight_smile: Although it is in cpp , may be you can take a look at it for ideas.

about that canvas thing, i’m pretty limited on screen real estate, hence i ruled out that solution in the very beginning.
And ive been keeping an eye on there for a while, i dont really know how to build it and use it, hence waiting for its official release

from krita import *
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import (QEventLoop, QTimer)
import os
_info = InfoObject()
running = False

def getPath(fullPath):
    for x in range (0,len(fullPath)):
        arrayIndex = len(fullPath)-x-1
        currentChar = fullPath[arrayIndex]
    
        if currentChar == '/':
            path = fullPath[0:arrayIndex+1]
            return path

def SaveFile(name,number):
    Krita.instance().activeDocument().setBatchmode(True)
    Krita.instance().activeDocument().exportImage(name+str(number)+".jpeg",_info)
    Krita.instance().activeDocument().setBatchmode(False)

def sleep(value):
    loop = QEventLoop()
    QTimer.singleShot(value, loop.quit)
    loop.exec()


def Start(name,delay, instance):
    path = getPath(Krita.instance().activeDocument().fileName())
    if os.path.isdir(path+"CrudeTimelapse/")==False:
        os.mkdir(path+"CrudeTimelapse/")
    finalPath = path + "CrudeTimelapse/" + name + ".jpeg"
    i=0
    running = True
    while(running):
        #Debug stuff OPEN
        if instance==True:
            if i>10:
                running = False
        #DebugStuff CLOSE

        #god please forgive me for this
        if Krita.instance().activeDocument().framesPerSecond() == 1:
            running = False
        
        i+=1
        SaveFile(finalPath,i)
        sleep(delay*1000)
        QApplication.instance().processEvents()
        
    
#Start("meh2",10,True)

So I’m done with this and it serves my purpose lol
i can finally get back to working.

if u find any bruteforcing (like how to stop the recording process)
please forgive, i started with python literally a month ago, and all my experience has been in bpy so far
My drawing sessions are usually 12-24 hours so i guess a 10-30 sec delay works, also, not really getting any performance issues at 1080p, so i guess its kinda okayish for now

Unfortunately it skips export when the brush is drawing on the canvas, which can or cannot be an issue, I’ll try and see how that goes

anyways, Thanks a lot for your help :smile:

Now just add code to take_snapshot() that saves the document or layer.
saving / exporting is kinda slow operation. It would be faster to just export the snapshot of “screen pixels”, but then there is the issue of translation / rotation / zoom.

from PyQt5.QtCore import Qt
from PyQt5.QtCore import QDateTime, QSize
from PyQt5.QtWidgets import QPushButton


class Timelapse(QPushButton):
    def __init__(self, interval=60000, parent=None):
        super(Timelapse, self).__init__(parent=parent)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self._interval = interval
        self._last_time = QDateTime.currentMSecsSinceEpoch()
        self._timer_id = self.startTimer(1000)  # ms running faster, so that button text can change.
        self.clicked.connect(self.on_clicked)
        self.setText("Hello!")

    def timerEvent(self, event):
        now_ms = QDateTime.currentMSecsSinceEpoch()
        time_left_ms = self._interval - (now_ms - self._last_time)
        time_left_sec = int(time_left_ms / 1000)
        self.setText("{} seconds to next snapshot\nclick me to stop.".format(time_left_sec))
        if time_left_ms <= 0:
            self.take_snapshot(now_ms)
            self._last_time = now_ms

    def take_snapshot(self, now_ms):
        self.setText("Say CHEECE!")

    def on_clicked(self, checked=None):
        self.killTimer(self._timer_id)
        self.close()
        print("bye!")

    def sizeHint(self):
        return QSize(300, 60)


button = Timelapse()
button.show()

/AkiR

oh, the canvas thing actually looks interesting,
it can actually be the best method for now
i hadnt read it before and replied, sorry for that,
also thanks for the link :smile:

And ive been keeping an eye on there for a while, i dont really know how to build it and use it, hence waiting for its official release

1 Like