Python QT6 programs not working, not in Thonny, not from terminal

Hi,
since almost 3 days I am trying to get my programs running with Qt6, but not in Thonny and not in terminal they get executed. Adaptions for Qt 6 are made (example exec_ to exec). If I change the Qt number in the imports from 6 to 5 (so Qt5), they are running.
All Qt6 packages are successfully installed and shown in Thonny config.

For no longer getting stopped in development, I changed to my Fedora Linux notebook and there the Qt6 installation ran smoothly and my same programs get executed perfectly. (so it’s not my programs fault :grinning:)

Any idea, any experiences what might be wrong?

Can you clearly state what happens when you try to run? Maybe post error message

thonny is a custom package - although unsupported - it most likely work.

But you have to realize that installing python packages is not done using pip.

Python packages installs using pacman and all has a prefix followed by the package name like this example python-<pkgname>

Manjaro is not Fedora - I played a little with qt6 a while ago - it is a fairly simple piece - just for the exercise.

https://github.com/fhdk/python-118dk

I just tested it still works as expected.

You may need to educate yourself

Using thonny usually implies creating a virtual environment. If the modules is not available in this environment you will get issues.


For the exercise - I tested my little phone/address parser - in thonny - with a little modification of the requirements.txt - I could install the necessary packages in the virtual environment and run the app.

With that in mind it is a local issue - most likely your configuration - I cannot say what or what not - but thonny can work on Manjaro

Summary

If you wanna try it out - there is three files

requirements.txt
PySide6
beautifulsoup4
certifi
phonebook.py
#!/usr/bin/python3
# -*- coding: utf8 -*-

# Copyright 2021 Frede Hundewadt
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and
# associated documentation files (the "Software"),
# to deal in the Software without restriction,
# including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from PySide6 import QtCore, QtWidgets
import sys
import parse_url


class Lookup(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.buttonLookup = QtWidgets.QPushButton("Lav opslag 118.dk")
        self.labelAddressHint = QtWidgets.QLabel("Husnummer kan bruges som afgrænsning på større områder.\nEksempel: Ryhaven, 8210 eller Bispehavevej 121, 8210")
        self.textAddress = QtWidgets.QLineEdit("vejnavn [nr], postnummer")
        self.textResult = QtWidgets.QTextEdit("")
        self.textResult.setReadOnly(True)
        self.textResult.setFontFamily("monospace")

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.labelAddressHint)
        self.layout.addWidget(self.textAddress)
        self.layout.addWidget(self.buttonLookup)
        self.layout.addWidget(self.textResult)

        self.buttonLookup.clicked.connect(self.lookup)

    @QtCore.Slot()
    def lookup(self):
        """ run lookup """
        self.textResult.clear()
        if self.textAddress.text() == "vejnavn [nr], postnummer":
            self.textResult.setPlainText("kan ikke findes")
            return
        # run lookup

        self.textResult.setPlainText("Vent venligst ...")
        results = parse_url.parse_url(self.textAddress.text())
        txt = ""
        for result in results:
            txt = f"{txt}Adresse : {result['address']}\n"
            txt = f"{txt}   Navn : {result['name']}\n"
            for number in result["phones"]:
                txt = f"{txt}    Tlf : {number}\n"
            txt = f"{txt}---------------------\n"
        self.textResult.setPlainText(txt)

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    widget = Lookup()
    widget.resize(600, 600)
    widget.show()
    sys.exit(app.exec())

parse_url.py
#!/usr/bin/python3
# -*- coding: utf8 -*-

# Copyright 2021 Frede Hundewadt
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and
# associated documentation files (the "Software"),
# to deal in the Software without restriction,
# including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from bs4 import BeautifulSoup
from operator import itemgetter
import argparse
import urllib.request
import urllib.parse
import urllib


# 118.dk
# person søgning : https://www.118.dk/search/go?pageSize=100&page=1&listingType=residential&where=
# firma søgning  : https://www.118.dk/search/go?pageSize=100&page=1&listingType=business&where=
# alle søgning   : https://www.118.dk/search/go?pageSize=100&page=1&listingType=&where=
# ingen grund til at fortælle at dette er et script
USER_AGENT = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246"}
SEARCH_118_URL = "https://www.118.dk/search/go?pageSize=100&page=1&listingType=residential&where="
SITE = "118.dk"

def parse_url(address):
    strings = list()
    entries = list()
    phones = list()
    contacts = list()
    url = f"{SEARCH_118_URL}{urllib.parse.quote_plus(address)}"
    req = urllib.request.Request(url=url, headers=USER_AGENT)
    with urllib.request.urlopen(req) as res:
        page = res.read()
        soup = BeautifulSoup(page, "html.parser", from_encoding='utf-8')
        for script in soup(["script", "style", "ul", "input", "form", "title", "ins", "h1", "h2", "h4",
                            "fieldset", "iframe", "strong", "img", "head", "meta", "link"]):
            script.extract()
        for string in soup.stripped_strings:
            if string.startswith("Geo") or \
                    string.startswith("FAG") or \
                    string.startswith("< til") or \
                    string.startswith("Tlf") or \
                    string.startswith("118.dk") or \
                    string.startswith("Vi kan") or \
                    string.startswith("Læs mere") or \
                    string.startswith("Copyright") or \
                    string.startswith("close") or \
                    string.startswith("Fjernelse") or \
                    string.startswith("Her kan") or \
                    string.startswith("Husnummer"):
                continue
            if string == "se kort":
                entries.append(tuple(strings))
                strings.clear()
            else:
                strings.append(string)

    for entry in entries:
        ad_protect = str(entry[1])
        if ad_protect.startswith("Reklame"):
            continue
        try:
            same_house = False
            number = entry[2]
            name = entry[0]
            iterate = [x for x in contacts if x["name"] == name]
            for c in iterate:
                if number not in c["phones"]:
                    same_house = True
                    c["phones"] = c["phones"] + [number]

            if not same_house:
                person = {
                    "name": entry[0],
                    "address": entry[1],
                    "phones": [number]
                }
                contacts.append(person)
        except (Exception,):
            continue

    return sorted(contacts, key=itemgetter("address"))

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-a", "--adresse", required=True, type=str, help="vejnavn [nr], postnummer")
    args = parser.parse_args()
    results = parse_url(args.adresse)

    for result in results:
        print(f"Adresse : {result['address']}")
        print(f"   Navn : {result['name']}")
        for number in result["phones"]:
            print(f"    Tlf : {number}")
        print(f"---------------------")

@linux-aarhus

Great help, thanks! So it should be an issue of my personal local install.
Will do the checks and tests…

@Thenujan

error message in Thony:

Process ended with exit code -6.

error message at start from terminal:

qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

zsh: IOT instruction (core dumped)  python3 ~/Nextcloud/Prog/py

PYQt6 wayland package is installed

again, changing the imports in my source back to Qt5, it runs

I get the same message on Plasma Wayland but my app runs nonetheless - no changes necessary.

[main] $ python phonebook.py 
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""

I remember there is a variable QT_QPA_PLATFORM - I don’t know how this works on Gnome Wayland but on Plasma it is not set - perhaps because Plasma is built using Qt toolkit.

[main] $ env | grep QT_
PLASMA_USE_QT_SCALING=1
QT_WAYLAND_FORCE_DPI=96
QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1
QT_AUTO_SCREEN_SCALE_FACTOR=0

There is a similar issue from june 2021 on arch bbs

https://bbs.archlinux.org/viewtopic.php?id=266967

correct, according to some hints and according to the archlinux wiki, it is QT_QPA_PLATFORM=wayland

when I set it temporarily with

export QT_QPA_PLATFORM=wayland

it doesn’t change anything

I wanted to set it into environment variables, but I don’t see a /etc/environment here

usually you set it in your homw ~/.profile

Done, no change.

What really puzzles me is that Qt Creator and Qt Designer are running fine and display they are using Qt6 6.4.1 (obviously on wayland…)

Have you tested my tiny PoC?

If that tiny can produce the small GUI then you narrow the troubleshooting down to your system

python phonebook.py

yes I did, and it opens up a small GUI with an entry line edit for number search I guess.
Looks like Danish or Norwegian language

It is danish.

So this opens - and it is very - very simple - it is entry edit for address - like the example above.

But the point was to establish which path your troubleshooting should go - and as you indicate that with qtcreator your code works - which leaves me with no more options.

I don’t know what is causing - I am leaning towards a code issue or missing module - python is usually very good at stacktrace for trouble shooting - this points more in the direction of qt library itself.

It could be as simple as a forgotten initialization - you will have start from the beginning - proof reading your code. Unitialized variables can make your code behave weird when expectations is not met.

I had to turn my world upside down at some point - instead of if clauses to validate - I began using try - except to catch where things was not as I expected.

There are some small changes in how the libraries are organized - nothing big - but likely enough. I would start with the imports - looking up the qt6 equivalents and verify it is holding.

Back in 2019 I was teaching myself pyqt5 - it was my first attempts to do gui with python.

https://github.com/fhdk/eordre3

One thing it took me a long time to figure out was how to handle hiDPI and how to initialize the app properly.

if __name__ == '__main__':
    app = QApplication(sys.argv)
    pixmap = QPixmap(":/splash/splash.png")
    splash = QSplashScreen(pixmap, Qt.WindowStaysOnTopHint)
    splash.show()

    app.processEvents()

    window = MainWindow()
    window.show()

    qtimer = QTimer()
    qtimer.singleShot(5000, window.run)
    splash.finish(window)

    sys.exit(app.exec_())

The imports was difficult as well

from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSlot, QCoreApplication
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QSplashScreen, \
    QTreeWidgetItem, QTableWidgetItem

The gui was created using qtcreator and then mogrified to python code with slots and signals - threads for download data in the background - quite an endeavour - just for the learning process.

@linux-aarhus

Exciting: when I replace the reference for the imports in my sources from “PyQt6” to “PySide6” (as you are using in your phonebook.py), then it runs in Thonny and from terminal!!

Wow!

And in Fedora it runs with “PyQt6” in the sources.

What does that tell us? Special implementation in Manjaro with PySide6 instead of PyQt6? With the need to change sources before distributing?

Thanks so much for your support so far!!

I don’t know why - but I didn’t realize you were using PyQt - I thought of the word as a reference - not the actual implementation

Default on Arch

PySide6 is the official implementation as it has no restrictions.

With PyQt you have to conform to licensing GPL and you cannot do anything commercial with PyQt. If you get paid to create the code - it is commercial - or it is used by a business - even only inhouse - it is commercial.

Which is why PySide6 is the defacto standard on Arch based distributions for Python.

Allright, got it!

Happy now.

That’s what Ilike in Manjaro: the direct contact and support!

(BTW I need to check if there is a hint in the archlinux wiki)

PySide6 · PyPI.

in archlinux.org wiki not directly stated
Python - ArchWiki

but when you scroll down to “Widget bindings”, you see a link to:

PyQt is created by Riverbank Computing | Introduction and is both a commercial offering and a GPL.

Depending on what you do - as I mentioned - if there is money involved (and closed source) - whether directly or indirectly - it becomes commercial - same as Qt toolkit from qt.io

one more observation:

if I use PyQt5 for the imports the programs are running, with PyQt6 not. So I guess with the step from 5 to 6 there must have been a decision of Manjaro project to go with PySide6. Just an assumption

There has been no decision on that - this is a decision of developers - developing apps - which toolkit they use - and thus which dependencies the app will pull upon install - so PyŚide6 being installed is no concious decision it is simply a matter of dependencies.

I remember PySide2 which was created as a opensource complement of PyQt5 - it was a long, long haul - and in my opinion as a developer it never reached the maturity I required which is why I used PyQt5 as the documentation was better - it was just a matter of choice.

With Qt6 - the Qt developers created PySide6 and is has matured side by side with Qt6.

PyQt6 is in the repo just as PySide6 are. See the search for PyQt6 Packages

This is what I mean by official as this has no restrictions upon as PyQt from Riverside Technologies has.

PySide6 is the official Python module from the Qt for Python project, which provides access to the complete Qt 6.0+ framework.

PySide6 versions following 6.0 use a C++ parser based on Clang. The Clang library (C-bindings), version 13.0 or higher is required for building. Prebuilt versions of it can be downloaded from download.qt.io.