Strange error in PyQt6 (not rendering radial gradients)

@martin, since using PyQt6, I am getting this strange error:
qt.svg: <input>:1:8277: Could not parse node: radialGradient

The QSvgRenderer (used by the QSvgWidget) can’t display radial gradients in PyQt6. This same code, that I have, worked perfectly fine in PyQt5, but now on PyQt6, it does not render radial gradients in SVGs anymore. Is there a workaround or something?

I guess you guys are on vacation. No problem. :wink:

1 Like

Hey @PedanticHacker yeah – I was out of the office during some renovations to the house, then that overran then I was on vacation. It ended up with a lot of time afk.

I wonder if there are just some bugs in Qt6 at the moment. Remember the weird glitching effects I saw on the QGraphicsScene while using SVG in Qt6? Though losing radial gradient support is a bit weird.

Can you share the SVG? Might be worth validating it.

Hi, @martin, I hope you had a wonderful time during vacation.

Well, the code for generating the SVG chessboard is on the python-chess’ GitHub site HERE.

The rendering part of the code for the generated python-chess SVG is HERE.

The board() method of the chess.svg module is the one that generates the SVG chessboard, based upon the arguments given to that method. For the check mark that is the check argument. The code given to the check argument of chess.svg.board() method is:

if self.board.is_check():
    return self.board.king(self.board.turn)

where self.board is chess.Board() instance.

The error, when there is check in a chessboard position, is this:

qt.svg: <input>:1:7114: Could not parse node: radialGradient
qt.svg: <input>:1:31331: Could not resolve property: #check_gradient

Below is a small test case example which works in PyQt5 (read on for the solution in PyQt6). Here’s the radial gradient (saved to a file named “gradient.svg”) and the test program.

image

The svg

<svg width="400" height="150">
 <defs>
    <radialGradient id="grad1"><stop offset="0%" stop-color="#ff0000" stop-opacity="1.0" /><stop offset="50%" stop-color="#e70000" stop-opacity="1.0" /><stop offset="100%" stop-color="#9e0000" stop-opacity="0.0" /></radialGradient>
 </defs>
 <ellipse fill="url(#grad1)" cx="200" cy="70" rx="85" ry="55"/>
 <text x="150" y="86" fill="green" font-family="Verdana" font-size="45">SVG</text>
</svg>

The test program

import math
import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView
from PyQt5.QtSvg import QGraphicsSvgItem

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.scene = QGraphicsScene()

        self.viewer = QGraphicsView()
        self.setCentralWidget(self.viewer)

        svg = QGraphicsSvgItem('gradient.svg')
        self.scene.addItem(svg)

        self.viewer.setScene(self.scene)


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

The same code in PyQt6, which just needs to change the import for from PyQt6.QtSvgWidgets import QGraphicsSvgItem

import math
import sys

from PyQt6.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView
from PyQt6.QtSvgWidgets import QGraphicsSvgItem

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.scene = QGraphicsScene()

        self.viewer = QGraphicsView()
        self.setCentralWidget(self.viewer)

        svg = QGraphicsSvgItem('gradient.svg')
        self.scene.addItem(svg)

        self.viewer.setScene(self.scene)


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

Switching out the radial gradient for a linear one, it works, e.g.

<svg width="400" height="150">
 <defs>
  <linearGradient id="grad1" y1="0%" x1="0%" y2="0%" x2="100%">
   <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1"/>
   <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1"/>
  </linearGradient>
 </defs>
 <ellipse fill="url(#grad1)" cx="200" cy="70" rx="85" ry="55"/>
 <text x="150" y="86" fill="green" font-family="Verdana" font-size="45">SVG</text>
</svg>

Looks like the following (in PyQt6)

image

I looked up some other radial gradients online, and some don’t throw the error. Porting back the attributes of the elements that work I found that adding an r (radius) attribute to the radialGradient will mean it doesn’t throw the error. The value necessary to give the expected gradient is 0.5

  <radialGradient id="grad1"  r="0.5">
    <stop offset="0%" stop-color="#ff0000" stop-opacity="1.0"/>
    <stop offset="50%" stop-color="#e70000" stop-opacity="1.0"/>
    <stop offset="100%" stop-color="#9e0000" stop-opacity="0.0"/>
   </radialGradient>

It makes some sense, in that a radial gradient doesn’t really make sense without a radius.

The result with the above

image

Final SVG code

<svg width="400" height="150">
 <defs>
 
  <radialGradient id="grad1" r="0.5">
    <stop offset="0%" stop-color="#ff0000" stop-opacity="1.0"/>
    <stop offset="50%" stop-color="#e70000" stop-opacity="1.0"/>
    <stop offset="100%" stop-color="#9e0000" stop-opacity="0.0"/>
   </radialGradient>
 </defs>
 <ellipse fill="url(#grad1)" cx="200" cy="70" rx="85" ry="55"/>
 <text x="150" y="86" fill="green" font-family="Verdana" font-size="45">SVG</text>
</svg>

Wow, @martin, you nailed it! I just needed to pass the r attribute with the value of 0.5 and now it works perfectly in PyQt6. Thank you, thank you, thank you, you are truly a master hacker! :+1:

EDIT: I have reported this “missing r attribute” bug in python-chess and the issue has already been fixed. However, the python-chess author (Niklas Fiekas) told me that if the r attribute is missing, it should default to 50%, as specified HERE, saying If the attribute is not specified, the effect is as if a value of '50%' were specified.. I don’t know what guys at Riverbank changed in PyQt6 to break this. :thinking: