Search code examples
pythonpyqtpysidepyside6qtextbrowser

QTextBrowser shows markdown on Mac but not on Windows


I'm building an app with Qt (pyside6) and packaging it with Pyinstaller. Everything works great on mac when running it through pycharm or when packaged, but the QTextBrowser doesn't render the sourced markdown file when it's running on my windows 10 machine.

I'm including a link to a github repo that I put together which is an extremely boiled down version of my app. It is simply a Qt interface displaying only a QTextBrowser which displays a markdown file located in a data folder. This is reproducing the same problem as my actual app. What am I missing to get the markdown file to show up when executing the app on a windows machine?

I did verify that the file paths were correct (I think), and when I get the source type from self.education_textbox.sourceType() it successfully prints out markdown resource. This leads me to believe it's not strictly a path issue because it would say UnknownResource if it wasn't getting the markdown file.

https://github.com/BigMoonTech/problem_reproduction.git

Here is the app.py:

import sys

from PySide6.QtWidgets import QMainWindow, QApplication

from MainWindow import UiMainWindow
from infrastructure.PathResolver import resolve_path


class MainWindow(QMainWindow, UiMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setup_ui(self)
        self.education_textbox.setSource(resolve_path('data/edu/FecalPositiveRoundworm.md'))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setApplicationDisplayName('MyApp')
    main_window = MainWindow()
    main_window.setWindowTitle('MyApp')
    main_window.show()
    sys.exit(app.exec())

Here is the MainWindow.py:

from PySide6.QtCore import QSize
from PySide6.QtWidgets import QWidget, QVBoxLayout, QStackedWidget, QGridLayout, QTextBrowser


class UiMainWindow(object):
    def setup_ui(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(720, 515)
        MainWindow.setMinimumSize(QSize(720, 515))

        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")

        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(u"verticalLayout")

        self.stackedWidget = QStackedWidget(self.centralwidget)
        self.stackedWidget.setObjectName(u"stackedWidget")

        self.page = QWidget()
        self.page.setObjectName(u"page")
        self.verticalLayout2 = QVBoxLayout(self.page)
        self.verticalLayout2.setObjectName(u"verticalLayout2")
        self.education_textbox = QTextBrowser(self.page)
        self.education_textbox.setObjectName(u"education_textbox")
        self.verticalLayout2.addWidget(self.education_textbox)


        self.education_textbox.setOpenLinks(False)

        self.stackedWidget.addWidget(self.page)
        self.verticalLayout.addWidget(self.stackedWidget)

        MainWindow.setCentralWidget(self.centralwidget)

        self.stackedWidget.setCurrentIndex(0)

And here is the utility script to resolve file paths, PathResolver.py:

import os
import sys


def resolve_path(path: str) -> str:
    if getattr(sys, "frozen", False):
        # If the 'frozen' flag is set, we are in bundled-app mode!
        resolved_path = os.path.abspath(os.path.join(sys._MEIPASS, path))
    else:
        # Normal development mode. Use os.getcwd() or __file__ as appropriate in your case...
        resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))

    return resolved_path

Solution

  • The .setSource() method accepts a QUrl as its first parameter.

    void QTextBrowser::setSource(const QUrl &url, QTextDocument::ResourceType type = QTextDocument::UnknownResource) Attempts to load the document at the given url with the specified type.

    While it's accepting the string, this is being interpreted as an Url. This is likely fine on non-Windows platforms, since the path separators are correct.

    To make it work cross-platform you should first create a QUrl object from the local file path, and pass that to setSource, i.e.

    from PySide6.QtCore import QUrl
    
    url = QUrl.fromLocalFile(path)
    self.education_textbox.setSource(url)
    

    Adding this step, the file loads correctly.

    Markdown file loading in the GUI window