Krita Photobash Plugin release 1.0!

Did a lot of improvements in the meantime, now it’s more usable with smaller widgets :smiley:

3 Likes

Awesome work dude, this looks great!

2 Likes

Hope? It’s really awesome :ok_hand:t2:

Adding images as references instead as layers would be cool too

1 Like

Huge thank you! I’m not familiar with the references workflow inside of Krita, so that’s why I didn’t support that

It’s OK. Great timesaver, let’s build some nice legooo landscape ! Haha

1 Like

Olá @slightlyangrydodo .
I just started testing it a bit and I have noticed some odd behaviors while I am using Krita 5 beta.

Behaviours:

  • There seems to be a lack of a manual. The desktop file is missing the entrance that allows the manual to be displayed.
  • it creates new layer but it applies the image to the node that is selected. you have to move up the layer stack but I think that is not possible unless the bug to do so was corrected.
  • Image scale seems unresponsive also fit to scale, as placing images or their representation are fixed size. If I am using it incorrectly I have no information on the manual to check.
  • On the code you seem to have something to center the image to the center of the document but the image locks to (zero, zero) and the document is big enough for it.
  • At the last page if it is lasting like 2 images the buttons become unalign. having the buttons inside a grid layout it helps the buttons underneath to become align.

Bugs:

  • I played with “image scale” and “fit image” and I got this:
ZeroDivisionError
Python 3.8.1: C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\bin\krita.exe
Sat Sep 25 20:29:43 2021

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py in <lambda>()
   92         self.imagesButtons[6].clicked.connect(lambda: self.buttonClick(6))
   93         self.imagesButtons[7].clicked.connect(lambda: self.buttonClick(7))
   94         self.imagesButtons[8].clicked.connect(lambda: self.buttonClick(8))
   95 
   96         mainLayout.addLayout(topLayout)
self = <photobash_images.photobash.PhotobashDocker object>
self.imagesButtons = [<PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>]
].clicked undefined
self.buttonClick = <bound method PhotobashDocker.buttonClick of <photobash_images.photobash.PhotobashDocker object>>

 C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py in buttonClick(self=<photobash_images.photobash.PhotobashDocker object>, position=8)
  191     def buttonClick(self, position):
  192         if position < len(self.foundImages) - len(self.imagesButtons) * self.currPage:
  193             self.addImageLayer(self.foundImages[position + len(self.imagesButtons) * self.currPage])
  194 
  195     def updateImages(self):
self = <photobash_images.photobash.PhotobashDocker object>
self.addImageLayer = <bound method PhotobashDocker.addImageLayer of <photobash_images.photobash.PhotobashDocker object>>
self.foundImages = ['G:/Frame Cutz/Vtuber/References/0086095a7e64d639e5d32b4c98c7a1b0.jpg', 'G:/Frame Cutz/Vtuber/References/0103e734b16cea22f3f828def3c67c5a.jpg', 'G:/Frame Cutz/Vtuber/References/02448d75e4f2bd0dd96a2e5e8b69f938.jpg', 'G:/Frame Cutz/Vtuber/References/04a70b4c56eef2239f8118f01a3e17f4.jpg', 'G:/Frame Cutz/Vtuber/References/04ccb9cc50203ef3ceaebcdac43494d5.jpg', 'G:/Frame Cutz/Vtuber/References/04f962839250c11c56139f11c879f10c.jpg', 'G:/Frame Cutz/Vtuber/References/06110e7cf8c6a72f3f2ff176970a1bd0.jpg', 'G:/Frame Cutz/Vtuber/References/06148f6ee4dfe71451032ec4dac8742a.jpg', 'G:/Frame Cutz/Vtuber/References/082d0967495d179d96e8664da0c83033.jpg', 'G:/Frame Cutz/Vtuber/References/08c282d90f5187e42441fd9fdb858b32.jpg', 'G:/Frame Cutz/Vtuber/References/098c07d5e484481126a95ea3e46ed533.jpg', 'G:/Frame Cutz/Vtuber/References/09dfac1b1107e0a23b4fb619670b90f2.jpg', 'G:/Frame Cutz/Vtuber/References/0a2f3c59ca4a12984b31f3fadb41f92a.jpg', 'G:/Frame Cutz/Vtuber/References/0a7dffb0fab5957e61553603d4530698.jpg', 'G:/Frame Cutz/Vtuber/References/0b1eeb5af64673a8b150e29aa91aa22b.jpg', 'G:/Frame Cutz/Vtuber/References/0b487d385b899b4afc1df248f476328c.jpg', 'G:/Frame Cutz/Vtuber/References/0d4ee170f6dbbacebe5925a97fa42dc8.jpg', 'G:/Frame Cutz/Vtuber/References/0df386230fac07a62e9f82e6a9094ad0.jpg', 'G:/Frame Cutz/Vtuber/References/0eb5cf46302d9f7e5d668f25e07cd238.jpg', 'G:/Frame Cutz/Vtuber/References/0f1a188d25d3ca32a9dedfc0f8c8ad08.jpg', ...]
position = 8
builtinlen = <built-in function len>
self.imagesButtons = [<PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>, <PyQt5.QtWidgets.QToolButton object>]
self.currPage = 0

 C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py in addImageLayer(self=<photobash_images.photobash.PhotobashDocker object>, photoPath='G:/Frame Cutz/Vtuber/References/082d0967495d179d96e8664da0c83033.jpg')
  255 
  256             if self.fitCanvasChecked:
  257                 if activeNode.bounds().width() / activeNode.bounds().height() > doc.bounds().width() / doc.bounds().height():
  258                     scalingFactor = doc.bounds().width() / activeNode.bounds().width()
  259                     newWidth = doc.bounds().width() * self.currImageScale / 100
activeNode = <PyKrita.krita.Node object>
activeNode.bounds = <built-in method bounds of Node object>
).width undefined
).height undefined
doc = <PyKrita.krita.Document object>
doc.bounds = <built-in method bounds of Document object>
ZeroDivisionError: division by zero
    __cause__ = None
    __class__ = <class 'ZeroDivisionError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of ZeroDivisionError object>
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
    __format__ = <built-in method __format__ of ZeroDivisionError object>
    __ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
    __gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of ZeroDivisionError object>
    __init_subclass__ = <built-in method __init_subclass__ of type object>
    __le__ = <method-wrapper '__le__' of ZeroDivisionError object>
    __lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
    __ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ('division by zero',)
    with_traceback = <built-in method with_traceback of ZeroDivisionError object>

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py", line 94, in <lambda>
    self.imagesButtons[8].clicked.connect(lambda: self.buttonClick(8))
  File "C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py", line 193, in buttonClick
    self.addImageLayer(self.foundImages[position + len(self.imagesButtons) * self.currPage])
  File "C:\Users\EyeOd\Desktop\krita-x64-5.0.0-beta1\share\krita\pykrita\photobash_images\photobash.py", line 257, in addImageLayer
    if activeNode.bounds().width() / activeNode.bounds().height() > doc.bounds().width() / doc.bounds().height():
ZeroDivisionError: division by zero

Suggestions

Some ideas you might like or not:

  • adding reference images instead of adding layers but I was not finding reference images in Python
  • maybe a counter of how many pages there are with images and in which page we are.
  • right click to open a menu on the buttons with a Context Menu if you add more options
  • Ability to just open a single image for reference within the docker.
  • I may be dreaming too much but maybe dragging and dropping a image with the mouse with event.mimeData().hasImage and it would be interesting to drop it on the canvas or on other dockers that accept images. Pigmento accepts images like that and it does feel proper to move it in and not use the explorer constantly.
  • if there is no document open you could open that document with that image also.
2 Likes

Yeah, I had the feeling a lot of things would break for 5.0… I’ll try to get back on the Krita horse soon, thanks for the feedback, and for the troubleshooting!

2 Likes

Okay, so I’m finally reading this more thoroughly, and I’m going to address your post, point by point.

Yup, this is clearly a bug. Fixing shouldn’t be too hard!

I actually think that this behaviour is best, as I could want to photobash an image with other things in front of it to occlude it. However I do see that this could be useful, so I’m thinking of adding a button to allow this.

This seems to be a bug for 5.0, will look into it.

Again, this seems like a 5.0 bug. Will look into it.

Yeah, this one is probably going to be a bit of a pain to do, but I’ll look into it after other more important things are fixed, since it’s relatively low priority.

Yeah, this is probably not going to be done. I could check how to add a reference image, but the point of this plugin is to photobash, and I want them to be regular paint layers so that I can do whatever I want with them, paint over, merge with other images, etc.

This seems relatively harmless and a nice QoL improvement, so I’ll add it in.

This is a great idea. I dislike the idea of having too many buttons cluttering up the UI, but this really could solve a lot of things. I feel this will be a pain to implement, but it’s a great addition, and already solves some design problems I can see.

I’m not sure I understand this one, like hovering to get a larger preview or something like that?

Yeah, this one isn’t goint to the list as of now. I think that a lot more than this needs to be implemented in order to work with good UX. Still, I’ll check some things out, but not for this release.

I have no idea how to do that, and I personally don’t think that this is worth the extra complexity. If I want to create a new artwork, I’ll create the canvas the way I want to first, and then fit the image on to that.

I have no ETA as to when things will be fixed, as I’m working a full-time job now, with other projects on the side (as well as fixing the UI Redesign plugin for 5.0). Still, when there’s news to share, I’ll make sure to tag you!

Thanks once again for this awesome, thorough feedback! :smiley:

1 Like

More like hidding the 9 buttons to show just one widget preview.

Well if you feel overwhelmed with time I could help by chip in with some code if you want and you can take a look after and change as you see fit.

Some of my suggestions I have them already made so giving a boost should not be too hard.

1 Like

I wouldn’t mind reviewing some stuff you made, I could always learn some more! What points did you address?

I mean, made for myself. I just scrap them out because they were of no use for my current project but I have notes on everything new I do.

I never edit anything of other people without having clearance to do so. It is very rude to edit stuff without the ok I think. I will start writing something today. I have to do a fork or something like that if I am not mistaken or is it a pull? I need to check how to do this. Github is still a mystery to me.

No fork needed! I haven’t added the license yet, but it will be MIT, so it’s very permissive. I can give you permissions to create branches, and so you can just create a branch on the repo, push your changes there, and then create a pull request. If you have any doubts, I’m happy to lend a hand!

I never heard about any of that before.
I am checking out how that goes now.

1 Like

I was just talking about the licenses of plugin in other thread, but I think Krita plugins need to be GPL licensed.

1 Like

Oh, didn’t know that, that’s nice to know, thanks!

Yeah the whole license discussion is here

To my knowledge, MIT is less restrictive than GPL, and is compatible with GPL

So for me it shouldn’t be a problem

Grum999

I know that it’s less restrictive, but if it’s GPL that forces everything that uses my code to be open-source (or GPL, not sure). I personally prefer MIT, but I can see the use case for GPL.

@slightlyangrydodo
I was taking a look at that and it seems you need to add me as a collaborator or something like that to a branch you create. or something like that. I will make some tweaks and when you have time to adjust that I will put in the files with the changes.

And you should add your license right away. I discovered yesterday I was applying my license incorrectly yesterday and was updating everything.

Yeah, I’ll send you more info in a PM

1 Like