2

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
musicamante
  • 41,230
  • 6
  • 33
  • 58
Moony
  • 37
  • 4
  • Does the issue happen only when packaged or also when run from a command prompt? Did you try to just set the mark down with a string or the contents through [`setMarkDown()`](https://doc.qt.io/qt-6/qtextedit.html#markdown-prop)? Also, before checking the content type, did you try to just *print* the content of the file? What Qt version are you using? – musicamante Jul 26 '22 at 04:39
  • It happens with and without packaging but only in windows. I use pycharm so I haven’t run it directly from the command line outside of the IDE. I haven’t directly used setMarkDown(). I did however read the file with open() and printed the contents to verify my path resolution was succeeding. I also printed the file lines with a critical message box when packaged as well. I’m using the most recent version of PySide6 (I think it wraps Qt 6.3 but could be wrong because I’m in bed now). I’ll be back at it in the morning. Thinking about switching to pyside2 to see if it happens still – Moony Jul 26 '22 at 05:37

1 Answers1

2

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

mfitzp
  • 15,275
  • 7
  • 50
  • 70
  • Specifically, when casting a string to a QUrl it will be parsed in [tolerant mode](https://doc.qt.io/qt-6/qurl.html#ParsingMode-enum), which percent-encodes backslashes (amongst other things). This means it's unreliable on *all* platforms, since a file-path could easily contain several other problematic characters (such as spaces) that will be automatically percent-encoded. – ekhumoro Jul 26 '22 at 10:58