Search code examples
pythonsvgpyqtpyqt5python-chess

How to display an SVG image in Python


I was following this tutorial on how to write a chess program in Python.

It uses the python-chess engine. The functions from that engine apparently return SVG data, that could be used to display a chessboard.

  • Code from the tutorial:
import chess
import chess.svg

from IPython.display import SVG

board = chess.Board()
SVG(chess.svg.board(board=board,size=400))  

but when I run that code, all I see is a line in the terminal and no image.

<IPython.core.display.SVG object>

The tutorial makes a passing reference to Jupyter Notebooks and how they can be used to display SVG images. I have no experience with Jupyter Notebooks and even though I installed the package from pip and I dabbled a little into how to use it, I couldn't make much progress with regards to my original chessboard problem. But what I do have, is, experience with Qt development using C++ and since Qt has Python bindings, I decided to use those bindings.

Here is what I wrote:

import sys
import chess
import chess.svg
from PyQt5 import QtGui, QtSvg
from PyQt5.QtWidgets import QApplication
from IPython.display import SVG, display

app = QApplication(sys.argv);

board = chess.Board(); 
svgWidget = QtSvg.QSvgWidget(chess.svg.board(board=board, size=400));
#svgWidget.setGeometry(50,50,759,668)
svgWidget.show()

sys.exit(app.exec_())

A Qt window opens and shows nothing and in the terminal I see a lot of text - (apparently the SVG data is ending up in the console and not in the Qt window that is opening?).

I figured I have to install some SVG library under python so I installed drawSvg from pip. But it seems that library generates SVG images. And was of no use for me.

What is even more strange is, after seeing this SO question, I tried the following:

import sys
import chess
import chess.svg
from PyQt5 import QtGui, QtSvg
from PyQt5.QtWidgets import QApplication
from IPython.display import SVG, display

app = QApplication(sys.argv);

board = chess.Board(); 
svgWidget = QtSvg.QSvgWidget('d:\projects\python_chess\Zeichen_123.svg');
#svgWidget.setGeometry(50,50,759,668)
svgWidget.show()

sys.exit(app.exec_())

And it showed an image - an SVG image! What is the difference then between my case and this case?

Question: So my question is, what I am doing wrong in the case of the chessboard SVG data? Is the SVG data generated by the python-chess library not compatible with QtSvg?


Solution

  • I think you are getting confused by the scripting nature of Python. You say, you have experience with Qt development under C++. Wouldn't you create a main window widget there first and add to it your SVG widget within which you would call or load SVG data?

    I would rewrite your code something like this.

    import chess
    import chess.svg
    
    from PyQt5.QtSvg import QSvgWidget
    from PyQt5.QtWidgets import QApplication, QWidget
    
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
    
            self.setGeometry(100, 100, 1100, 1100)
    
            self.widgetSvg = QSvgWidget(parent=self)
            self.widgetSvg.setGeometry(10, 10, 1080, 1080)
    
            self.chessboard = chess.Board()
    
            self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8")
            self.widgetSvg.load(self.chessboardSvg)
    
    if __name__ == "__main__":
        app = QApplication([])
        window = MainWindow()
        window.show()
        app.exec()
    

    EDIT

    It would be even better if you would add a paint function to the MainWindow class. Because for sure in future, you would want to repaint your board image many times, whenever you would move a piece. So I would do something like this.

         def paintEvent(self, event):
             self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8")
             self.widgetSvg.load(self.chessboardSvg)