Lazy Text Tool(Prototype) – A plugin that helps you type

Good news, 5.0 changes are live (expect lots of issues :wink: ). 5.0 changes get rid of a lot of the workarounds like the copy and paste hack and the like. It also introduces the ability to modify transformed objects. And it won’t do removal of layers like before but actually edit the layer and keep all your masks or anything else attached to the layer.

Another thing, you can now Ctrl + click to allow multiple texts on the same layer or add text to layers that already have other shapes or the like. In that case, layer name management will not be handled by the plugin anymore.

IMPORTANT: If you experience issues with Krita 5.0 beta, make sure you remove this plugin first to be sure it isn’t the culprit. Or better yet, use a separate instance for this plugin.

Download is same link as above:

Also, an important note for those on Krita 4.0, while I tried to make it backwards compatible, I think I broke something :stuck_out_tongue: I will fix these issues but after that, all development on 4.0 prototype will cease. If you are looking for the 4.0 version before the 5.0 changes, here it is:

UPDATE: The Krita 4 fixes have been applied, so try this only if you have problems with the new one.

7 Likes

In latest 5.0 nightly I cannot use the plugin. (“AttributeError:‘NoneType’ object has no attribute ‘currentIndex’”) Is it that it’s built for the beta, and not for nightlies?

Can you give the entire error and what you did?

1 Open Krita
2 Create a document (Apparently you can toggle it if there’s no document, but the error will pop up eventually when you try to use the feature)
3 Toggle Lazy Text Tool
4 Meet the error message
https://cdn.discordapp.com/attachments/838374815211061291/879740444685242408/error_1.txt
5 Close the message and meet an another one
https://cdn.discordapp.com/attachments/838374815211061291/879740448732753970/error_2.txt
6 Close the message and watch Krita crash

I think I know what the issue is but to confirm just in case, are you using any non-default ui setting like detached canvas? (I was able to replicate your error with that option enabled, but want to be sure there is no other options that may do similar stuff)

No I’m not. There maybe are options I use and I’m not aware of though.

I use UI redesign plugin, but Krita still throws error and crashes when I try to use the feature(=when I select the Text tool after toggling it) with that plugin disabled.

What about the multidocument setting? Is it on tabs or subwindows? I did a change for that so see if it works.

It’s set to subwindows.

There is the problem, redownload the latest and see if it fixes your problem

There is some discussion related to this plugin and Japanese style vertical typing in this thread - Lazy Text Tool Plugin and japanese vertical text

1 Like

The issue is now fixed. Thanks :smiley:

But the plugin is still very buggy and doesn’t feel like it’s very user friendly. I’m not sure how much it can be done with python code but I now think this whole thing should be implemented as official feature(or at least other methods to edit multiple texts fast should be implemented…), not a plugin that is separately managed and maintained.

But I’ll try to report bugs and papercuts with this plugin as much as possible.

I applied the Krita 4 fixes, so probably it should be okay to use the latest version on Krita 4 and 5. (Since it contains some bug fixes in between implementing Krita 5, it probably should be better for Krita 4 too but it would be up to people to decide)

That said, I am most likely going to focus on Krita 5 fixes only going forward.

I ran into this problem when I tested the plugin:

AttributeError
Python 3.8.1: C:\GAMES\STEAM\steamapps\common\Krita\krita\bin\krita.exe
Sat Sep 4 16:13:30 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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py in mousePressEvent(self=<LazyTextTool.LazyTextToolFunc.LazyTextScene object>, event=<PyQt5.QtWidgets.QGraphicsSceneMouseEvent object>)
2442 else:
2443 print (“WRITE ITEM!”)
2444 self.parent().writeItem(self.lastSelectedObject)
2445 self.setCurrentMode(self.INIT_MODE)
2446 return
self = <LazyTextTool.LazyTextToolFunc.LazyTextScene object>
self.parent =
).writeItem undefined
self.lastSelectedObject = <LazyTextTool.LazyTextToolFunc.LazyTextHandle object>

C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py in writeItem(self=<LazyTextTool.LazyTextTool.LazyTextTool.TextCanvas object>, textObject=<LazyTextTool.LazyTextToolFunc.LazyTextHandle object>)
328 currentLayer = self.textTool.currentLayer
329
330 if textObject.textItem.toPlainText() == ‘’:
331 self.helperDialog.target = None
332 self.scene.removeItem(textObject)
textObject = <LazyTextTool.LazyTextToolFunc.LazyTextHandle object>
textObject.textItem undefined
AttributeError: ‘LazyTextHandle’ object has no attribute ‘textItem’
cause = None
class = <class ‘AttributeError’>
context = None
delattr = <method-wrapper ‘delattr’ of AttributeError object>
dict = {}
dir =
doc = ‘Attribute not found.’
eq = <method-wrapper ‘eq’ of AttributeError object>
format =
ge = <method-wrapper ‘ge’ of AttributeError object>
getattribute = <method-wrapper ‘getattribute’ of AttributeError object>
gt = <method-wrapper ‘gt’ of AttributeError object>
hash = <method-wrapper ‘hash’ of AttributeError object>
init = <method-wrapper ‘init’ of AttributeError object>
init_subclass =
le = <method-wrapper ‘le’ of AttributeError object>
lt = <method-wrapper ‘lt’ of AttributeError object>
ne = <method-wrapper ‘ne’ of AttributeError object>
new =
reduce =
reduce_ex =
repr = <method-wrapper ‘repr’ of AttributeError object>
setattr = <method-wrapper ‘setattr’ of AttributeError object>
setstate =
sizeof =
str = <method-wrapper ‘str’ of AttributeError object>
subclasshook =
suppress_context = False
traceback =
args = (“‘LazyTextHandle’ object has no attribute ‘textItem’”,)
with_traceback =

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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py”, line 2444, in mousePressEvent
self.parent().writeItem(self.lastSelectedObject)
File “C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py”, line 330, in writeItem
if textObject.textItem.toPlainText() == ‘’:
AttributeError: ‘LazyTextHandle’ object has no attribute ‘textItem’

In addition, I also know whether it is possible to change the py file to adjust the location of docker? It overlaps with the ui plugin
image

1 Like

Okay, I did a fix which should hopefully fix it. Though to note it helps to mention what exactly was being done to get the error and what the intention was.

As for your question about moving the thing. After:

uic.loadUi(os.path.dirname(os.path.realpath(__file__)) + '/LazyTextToolHelper.ui', self)

do

uic.loadUi(os.path.dirname(os.path.realpath(__file__)) + '/LazyTextToolHelper.ui', self)
self.move(200,0)

Where 200 is how many pixels to move it by horizontally and 0 how much to move it vertically.

In the final version, I probably will make it a proper docker and add a float quick menu.

1 Like

Thank you, I set it to 80 and it works well.
In addition, I triggered another errors:

IndexError
Python 3.8.1: C:\GAMES\STEAM\steamapps\common\Krita\krita\bin\krita.exe
Sat Sep 4 21:29:51 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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py in layerChanged(self=<LazyTextTool.LazyTextTool.LazyTextTool object>, selected=<PyQt5.QtCore.QItemSelection object>, deselected=<PyQt5.QtCore.QItemSelection object>)
663 print (“ONTAB”, self.onTab, self.mdi.findChild(QtWidgets.QTabBar).currentIndex() )
664 if self.onTab != self.mdi.findChild(QtWidgets.QTabBar).currentIndex():
665 self.documentChanged()
666
667 if self.currentTextCanvas is None: return
self = <LazyTextTool.LazyTextTool.LazyTextTool object>
self.documentChanged = <bound method LazyTextTool.documentChanged of <LazyTextTool.LazyTextTool.LazyTextTool object>>

C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py in documentChanged(self=<LazyTextTool.LazyTextTool.LazyTextTool object>)
654 LazyTextTool.closeTextCanvas(self)
655 if textToolButton.isChecked():
656 LazyTextTool.openTextCanvas(self)
657
658 def layerRemoved(self):
global LazyTextTool = <class ‘LazyTextTool.LazyTextTool.LazyTextTool’>
LazyTextTool.openTextCanvas =
self = <LazyTextTool.LazyTextTool.LazyTextTool object>

C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py in openTextCanvas(self=<LazyTextTool.LazyTextTool.LazyTextTool object>)
579 if self.currentLayer.type() == ‘vectorlayer’:
580 shapes = self.currentLayer.shapes()
581 self.currentTextCanvas.fillLayer(self.currentLayer, shapes)
582
583
self = <LazyTextTool.LazyTextTool.LazyTextTool object>
self.currentTextCanvas = <LazyTextTool.LazyTextTool.LazyTextTool.TextCanvas object>
self.currentTextCanvas.fillLayer = <bound method LazyTextTool.TextCanvas.fillLayer …ool.LazyTextTool.LazyTextTool.TextCanvas object>>
self.currentLayer = <PyKrita.krita.VectorLayer object>
shapes =

C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py in fillLayer(self=<LazyTextTool.LazyTextTool.LazyTextTool.TextCanvas object>, layer=<PyKrita.krita.VectorLayer object>, shapes=)
70
71 def fillLayer(self, layer, shapes):
72 self.textObjectFromLayerAndShape([ layer, shapes[0] ])
73
74 def textObjectFromLayerAndShape(self, textItem):
self = <LazyTextTool.LazyTextTool.LazyTextTool.TextCanvas object>
self.textObjectFromLayerAndShape = >
layer = <PyKrita.krita.VectorLayer object>
shapes =
IndexError: list index out of range
cause = None
class = <class ‘IndexError’>
context = None
delattr = <method-wrapper ‘delattr’ of IndexError object>
dict = {}
dir =
doc = ‘Sequence index out of range.’
eq = <method-wrapper ‘eq’ of IndexError object>
format =
ge = <method-wrapper ‘ge’ of IndexError object>
getattribute = <method-wrapper ‘getattribute’ of IndexError object>
gt = <method-wrapper ‘gt’ of IndexError object>
hash = <method-wrapper ‘hash’ of IndexError object>
init = <method-wrapper ‘init’ of IndexError object>
init_subclass =
le = <method-wrapper ‘le’ of IndexError object>
lt = <method-wrapper ‘lt’ of IndexError object>
ne = <method-wrapper ‘ne’ of IndexError object>
new =
reduce =
reduce_ex =
repr = <method-wrapper ‘repr’ of IndexError object>
setattr = <method-wrapper ‘setattr’ of IndexError object>
setstate =
sizeof =
str = <method-wrapper ‘str’ of IndexError object>
subclasshook =
suppress_context = False
traceback =
args = (‘list index out of range’,)
with_traceback =

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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py”, line 665, in layerChanged
self.documentChanged()
File “C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py”, line 656, in documentChanged
LazyTextTool.openTextCanvas(self)
File “C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py”, line 581, in openTextCanvas
self.currentTextCanvas.fillLayer(self.currentLayer, shapes)
File “C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextTool.py”, line 72, in fillLayer
self.textObjectFromLayerAndShape([ layer, shapes[0] ])
IndexError: list index out of range

AttributeError
Python 3.8.1: C:\GAMES\STEAM\steamapps\common\Krita\krita\bin\krita.exe
Sat Sep 4 21:48:08 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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py in setCurrentFont(self=<LazyTextTool.LazyTextToolFunc.LazyTextHelper object>, font=<PyQt5.QtGui.QFont object>)
1504 def setCurrentFont(self,font):
1505 if self.blockMode: return
1506 tcursor = self.getCursor()
1507 fmt = QtGui.QTextCharFormat()
1508 fmt.setFontFamily(font.family())
tcursor undefined
self = <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>
self.getCursor = <bound method LazyTextHelper.getCursor of <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>>

C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py in getCursor(self=<LazyTextTool.LazyTextToolFunc.LazyTextHelper object>)
1592
1593 def getCursor(self):
1594 tcursor = self.target.textCursor()
1595 if (not tcursor.hasSelection()):
1596 tcursor.select(QtGui.QTextCursor.WordUnderCursor);
tcursor undefined
self = <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>
self.target = None
self.target.textCursor undefined
AttributeError: ‘NoneType’ object has no attribute ‘textCursor’
cause = None
class = <class ‘AttributeError’>
context = None
delattr = <method-wrapper ‘delattr’ of AttributeError object>
dict = {}
dir =
doc = ‘Attribute not found.’
eq = <method-wrapper ‘eq’ of AttributeError object>
format =
ge = <method-wrapper ‘ge’ of AttributeError object>
getattribute = <method-wrapper ‘getattribute’ of AttributeError object>
gt = <method-wrapper ‘gt’ of AttributeError object>
hash = <method-wrapper ‘hash’ of AttributeError object>
init = <method-wrapper ‘init’ of AttributeError object>
init_subclass =
le = <method-wrapper ‘le’ of AttributeError object>
lt = <method-wrapper ‘lt’ of AttributeError object>
ne = <method-wrapper ‘ne’ of AttributeError object>
new =
reduce =
reduce_ex =
repr = <method-wrapper ‘repr’ of AttributeError object>
setattr = <method-wrapper ‘setattr’ of AttributeError object>
setstate =
sizeof =
str = <method-wrapper ‘str’ of AttributeError object>
subclasshook =
suppress_context = False
traceback =
args = (“‘NoneType’ object has no attribute ‘textCursor’”,)
with_traceback =

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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py”, line 1506, in setCurrentFont
tcursor = self.getCursor()
File “C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py”, line 1594, in getCursor
tcursor = self.target.textCursor()
AttributeError: ‘NoneType’ object has no attribute ‘textCursor’

First error is fixed, 2nd error, what did you do to get that one?

I simply created two texts.
It is also possible that I changed the number in self.opacityEffect.setOpacity(0.5) to 1.0? I didn’t like it being translucent so I made the change, but it will be back soon. Is it possible that it triggered a bug?

Another bug:
Whenever I create a text box, the font size will randomly decrease. Then one time when I clicked the icon to increase the font, it appeared:

AttributeError
Python 3.8.1: C:\GAMES\STEAM\steamapps\common\Krita\krita\bin\krita.exe
Sat Sep  4 22:58:11 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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py in setCurrentFontSize(self=<LazyTextTool.LazyTextToolFunc.LazyTextHelper object>, fontSize=1.9)
 1278     def setCurrentFontSize(self,fontSize):
 1279         if self.blockMode: return
 1280         tcursor = self.getCursor()
 1281         fmt = QtGui.QTextCharFormat()
 1282         print ("SETMYFONT")
tcursor undefined
self = <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>
self.getCursor = <bound method LazyTextHelper.getCursor of <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>>

 C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py in getCursor(self=<LazyTextTool.LazyTextToolFunc.LazyTextHelper object>)
 1591 
 1592     def getCursor(self):
 1593         tcursor = self.target.textCursor()
 1594         if (not tcursor.hasSelection()):
 1595             tcursor.select(QtGui.QTextCursor.WordUnderCursor);
tcursor undefined
self = <LazyTextTool.LazyTextToolFunc.LazyTextHelper object>
self.target = None
self.target.textCursor undefined
AttributeError: 'NoneType' object has no attribute 'textCursor'
    __cause__ = None
    __class__ = <class 'AttributeError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of AttributeError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of AttributeError object>
    __doc__ = 'Attribute not found.'
    __eq__ = <method-wrapper '__eq__' of AttributeError object>
    __format__ = <built-in method __format__ of AttributeError object>
    __ge__ = <method-wrapper '__ge__' of AttributeError object>
    __getattribute__ = <method-wrapper '__getattribute__' of AttributeError object>
    __gt__ = <method-wrapper '__gt__' of AttributeError object>
    __hash__ = <method-wrapper '__hash__' of AttributeError object>
    __init__ = <method-wrapper '__init__' of AttributeError object>
    __init_subclass__ = <built-in method __init_subclass__ of type object>
    __le__ = <method-wrapper '__le__' of AttributeError object>
    __lt__ = <method-wrapper '__lt__' of AttributeError object>
    __ne__ = <method-wrapper '__ne__' of AttributeError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of AttributeError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of AttributeError object>
    __repr__ = <method-wrapper '__repr__' of AttributeError object>
    __setattr__ = <method-wrapper '__setattr__' of AttributeError object>
    __setstate__ = <built-in method __setstate__ of AttributeError object>
    __sizeof__ = <built-in method __sizeof__ of AttributeError object>
    __str__ = <method-wrapper '__str__' of AttributeError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ("'NoneType' object has no attribute 'textCursor'",)
    with_traceback = <built-in method with_traceback of AttributeError 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\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py", line 1280, in setCurrentFontSize
    tcursor = self.getCursor()
  File "C:\Users\123\AppData\Roaming\krita\pykrita\LazyTextTool\LazyTextToolFunc.py", line 1593, in getCursor
    tcursor = self.target.textCursor()
AttributeError: 'NoneType' object has no attribute 'textCursor'

Other questions:

  1. Every time the software is restarted, the “√” of the plug-in will disappear
  2. Adjusting the font size will cause the characters in this line to change together. This may be a deliberate setting. But this is inconsistent with people’s perception. If someone needs to make characters of different sizes. One mistake will cause all the words to become the same size
  3. After writing the text, if not click outside to confirm. Switching other tools will cause the text to be lost. I think an automatic confirmation should be added.

Okay, I fixed the issue with the adjusting text size. As for the other issue, I can’t replicate it :confused: can you give exact step by step?

  1. Yes, that is intentional cause this is a prototype.
  2. It is intentional and it is exactly how Krita’s text tool works too. There is always ctrl+z to undo
  3. Since I don’t like obstructive dialogs, I just made it commit if you change tools.