Python crashing when running a PyQt5 application

0

I am building a application with the backend done in python and the front end done in QML using PyQt5. When I run my application sometimes it runs with no problem but 80% of the time python crashes. When it crashed this message will be shown, Process finished with exit code -1073741819 (0xC0000005).

I have figured out that it crashes at this point in main: engine.rootContext().setContextProperty("con", main) It seems like when it tries to connect to the backend connections in my main QML file it fails. Does anyone know why this may be?

This is my main file:

# This Python file uses the following encoding: utf-8
import sys
import os
import threading
import time
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Signal, Slot, QTimer
from python_files.GetSearchResults import GetSearchResults
from python_files.StreamQueue import StreamQueue
from python_files.StreamGenerator import StreamGenerator
from python_files.DatabaseHelper import DatabaseHelper


class MainWindow(QObject):

    def __init__(self):
        QObject.__init__(self)
        self.timer = QTimer()
        self.timer.timeout.connect(lambda: self.update_progress())
        self.timer.start(500)
        self.play_img_val = 0
        self.database = DatabaseHelper()
        self.get_search_results = GetSearchResults()
        self.stream_queue = StreamQueue(self)
        self.stream_generator = StreamGenerator()
        self.queue_thread = None

    searchResult = Signal(int, str, str)
    updateQueue = Signal(str, str, str)
    setPlayer = Signal(str, str, str, str, str, str, str)
    setProgress = Signal(str, float)
    clearSearch = Signal()
    upDatePlayImg = Signal(str)
    setFavorites = Signal(str, str, str, str)
    clearFavorites = Signal()

    def add_stream_queue(self, stream, pos=None):
        self.stream_queue.add_to_queue(stream, pos)
        self.update_onscreen_queue()

    def play_queue(self):
        if self.stream_queue.get_first_stream():
            self.queue_thread = threading.Thread(target=self.stream_queue.play_stream_queue, daemon=True)
            self.queue_thread.start()
            # self.update_player()

    def update_player(self):
        main_color, secondary_color, third_color = self.stream_queue.get_current_stream().get_colors()
        track = self.stream_queue.get_current_stream().get_track()
        artist = self.stream_queue.get_current_stream().get_artist()
        album_art = self.stream_queue.get_current_stream().get_album_art()
        length = self.stream_queue.get_current_stream().get_length()
        self.setPlayer.emit(album_art.replace("https://", "http://"), track, artist, length, main_color,
                            secondary_color, third_color)

    def update_progress(self):
        # print(self.stream_queue.get_current_time(), self.stream_queue.get_percentage())
        self.setProgress.emit(self.stream_queue.get_current_time(), self.stream_queue.get_percentage())

    # LOWER QUALITY OF IMAGE
    def update_onscreen_queue(self):
        stream_queue = self.stream_queue.get_queue()
        for stream in stream_queue:
            self.updateQueue.emit(stream.get_album_art().replace("https://", "http://"), stream.get_track(),
                                  stream.get_artist())

    @Slot(int)
    def add_to_queue(self, index):
        search_result = self.get_search_results.select_result(index)
        # stream = Stream(search_result.get_title(), search_result.get_link())
        stream = self.stream_generator.create_stream(search_result.get_title(), search_result.get_link())
        self.stream_queue.add_to_queue(stream)
        # self.update_onscreen_queue()
        self.updateQueue.emit(stream.get_album_art().replace("https://", "http://"), stream.get_track(),
                              stream.get_artist())
        self.play_queue()

    @Slot(str)
    def add_fav_queue(self, yt_id):
        print("########", yt_id)
        fav_stream = self.database.get_stream(yt_id)
        stream = self.stream_generator.create_db_stream(fav_stream[0], fav_stream[1], fav_stream[2], fav_stream[3],
                                               fav_stream[4], fav_stream[5], fav_stream[6], fav_stream[7],
                                               fav_stream[8], fav_stream[9], fav_stream[10], fav_stream[11])
        self.stream_queue.add_to_queue(stream)
        self.updateQueue.emit(stream.get_album_art().replace("https://", "http://"), stream.get_track(),
                              stream.get_artist())
        self.play_queue()


    @Slot(str)
    def search(self, search_query):
        self.get_search_results.search(search_query)
        search_results = self.get_search_results.get_results()
        self.clearSearch.emit()
        for i in range(len(search_results)):
            self.searchResult.emit(i, search_results[i].get_thumbnail(), search_results[i].get_title())

    @Slot(int)
    def save_stream(self, index):
        # ADD Multithreading
        search_result = self.get_search_results.select_result(index)
        stream = self.stream_generator.create_stream(search_result.get_title(), search_result.get_link())
        stream_id = self.database.get_last_id() + 1
        self.database.save_stream(stream_id, stream.get_title(), stream.get_link(), stream.get_audio_url(),
                                  stream.get_track(), stream.get_artist(), stream.get_album(), stream.get_album_art(),
                                  stream.get_length(), stream.get_year(), stream.get_main_color(),
                                  stream.get_second_color(), stream.get_third_color())

    @Slot()
    def get_favorites(self):
        self.clearFavorites.emit()
        favorites = self.database.get_streams()
        for favorite in favorites:
            self.setFavorites.emit(favorite[0], favorite[1], favorite[2], favorite[3])

    @Slot(float)
    def seek(self, seek_time):
        # print(seek_time)
        self.stream_queue.seek_to_time(seek_time)

    @Slot()
    def pause_stream(self):
        self.stream_queue.pause_stream()
        if self.play_img_val == 0:
            self.play_img_val = 1
            print("pause_icon")
            self.upDatePlayImg.emit("pause_icon.png")
        else:
            self.play_img_val = 0
            print("play_icon")
            self.upDatePlayImg.emit("play_icon.png")

    @Slot()
    def prev_stream(self):
        self.stream_queue.previous_stream()
        # self.update_player()

    @Slot()
    def next_stream(self):
        self.stream_queue.next_stream()
        # self.update_player()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    main = MainWindow()
    engine.load(os.path.join(os.path.dirname(__file__), "qml/main.qml"))
    engine.rootContext().setContextProperty("con", main)

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

This is my main QML file

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import "controls"
import QtGraphicalEffects 1.0

Window {
    id: mainWindow
    .
    .
    .

    flags: {
        Qt.Window | Qt.FramelessWindowHint;
        Qt.Window | Qt.MSWindowsFixedSizeDialogHint;
    }

    Rectangle {
        id: mainBackground
        .
        .
        .
    }

    Connections{
        target: con

        function onSearchResult(index, thumb, title){
            //listModel.append({index: index, name: title, thumb: thumb})

        }

        function onUpdateQueue(albumArt, track, artist){
            queueListModel.append({queueImg: albumArt, track: track, artist: artist});
        }

        function onSetPlayer(albumArt, track, artist, length, color1, color2, color3){
            albumImg.source = albumArt;
            trackTxt.text = track;
            artistTxt.text = artist;
            runTimeTxt.text = length;
            mainColor = color1;
            secondaryColor = color2;
            thirdColor = color3;
        }

        function onSetProgress(time, precentage){
            currTimeTxt.text = time;
            progressBar.value = precentage;
        }

        function onClearSearch(){
            //listModel.clear();
        }

        function onUpDatePlayImg(imgPath){
            playBtn.image1 = imgPath;
        }

        function onSetFavorites(yt_id, track, artist, album_art){
            //listModel.append({index: yt_id, name: track, thumb: album_art});
        }

        function onClearFavorites(){
            //listModel.clear();
        }

    }
}
python
pyqt5
qml
asked on Stack Overflow May 1, 2021 by PhonyStark

1 Answer

0

You likely are running into issues with threading. You are starting up your player in a different thread and then interacting with it from the main thread.

Generally speaking, any interactions with the Qt/QML framework (and most GUI frameworks) must be done from the main thread only. So, if for instance your streamer running in a different thread has a callback back over to Qt/QML, you must rework this into a call into Qt/QML on the main thread (a signal and slot is a good way to do that).

If you attempt to call Qt/QML code from a thread other than the main one you will get crashes.

Likewise, since you started the streamer in a different thread, you have to make sure that manipulating it from the main thread is supported. I.e. does that streamer support multiple threads changing its state at the same time?

So, bottom line – either get rid of the threading in your code or make sure that you are not accessing a non-thread safe body of code from more than one thread.

answered on Stack Overflow May 3, 2021 by David K. Hess • edited May 4, 2021 by David K. Hess

User contributions licensed under CC BY-SA 3.0