Search code examples
pythonpyqtpyqt5qresourceqdesktopservices

Show a local html file using QDesktopService


I would like to show a local html file when a user clicks on a help icon. The method shown below is connected to the triggered signal from the icon. In my method shown below, the html file is not being opened in my default browser and the except part of the script is not being activated. I have two questions:

  1. What is the best approach to showing a local html file with PyQt5?

  2. How to make the script throw an exception when a html file is not located?

    def helpScreen(self):
        try:
            urlLink = QUrl.fromLocalFile(':/plugins/geomAttribute/help/index_en.html')
            QDesktopServices.openUrl(urlLink)
        except:
            QMessageBox.warning(None, 'Warning', 'Unable to locate help file')
    

Solution

  • Why is HTML not shown?

    The path starts with : which indicates that you are using qresource, the first thing you must do is convert the .rc to .py with the command:

    pyrcc your_resource.qrc -o your_resource_rc.py
    

    In my case my qresource is resource.qrc generating the resource_rc.py file so you must import it into the .py.

    The qresource paths are virtual, they do not exist in the hard disk so when wanting to use that file the browser will not find it, so the solution is to convert it into a local file, we can save it with a QFile but this file must be temporary so what better is to save it with a QTemporaryFile.

    In your case, the code should be the following:

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Widget, self).__init__(parent)
            lay = QtWidgets.QVBoxLayout(self)
            button = QtWidgets.QPushButton("Help")
            button.clicked.connect(self.helpScreen)
            lay.addWidget(button)
    
        def helpScreen(self):
            resource_path = ":/plugins/geomAttribute/help/index_en.html"
            resource_file = QtCore.QFile(resource_path)
            if resource_file.open(QtCore.QIODevice.ReadOnly):
                tmp_file = QtCore.QTemporaryFile(self)
                tmp_file.setFileTemplate("XXXXXX.html")
                if tmp_file.open():
                    tmp_file.write(resource_file.readAll())
                    resource_file.close()
                    tmp_file.flush()
                url = QtCore.QUrl.fromLocalFile(tmp_file.fileName())
                if QtGui.QDesktopServices.openUrl(url):
                    return
            QtWidgets.QMessageBox.warning(None, 'Warning', 'Unable to locate help file')
    
    
    import resource_rc
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    1. What is the best approach to showing a local html file with PyQt5?

    There are several ways to display an HTML and the choice of the best depends on you, for example there are the following methods:

    • QDesktopServices::openUrl()
    • QLabel
    • QTextEdit, QPlainTextEdit, etc.
    • QWebEngineView, QWebView, etc.

    2. How to make the script throw an exception when a html file is not located?

    Qt for efficiency reasons will not throw exceptions, so you do not use try-except in the part of the code that directly depends on Qt, Qt has 2 main mechanisms to notify you that it is wrong, if the task is synchronous the function will return a Boolean that indicates that the task finished correctly or not, and if the error is given asynchronously will issue a signal indicating it, in the case of QDesktopServices::openUrl() is a synchronous task so it will return a boolean indicating if the task was executed successfully:

    bool QDesktopServices::openUrl(const QUrl &url)

    Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.

    [...]