简体   繁体   中英

Pyqt5 threading can't work (Fatal Python error: could not acquire lock)

I have read bunch of QThread tutorials and SO Answers about this. But I can't make my threading works. Most of the time, it just run once or it print error message. Threading works if I started it immediately after the app start, but I want the thread to run after certain function, cause I want to set the directory location first.

The file structure is arranged like this:

App.py
Controllers/
    main_controller.py
    recorder.py
Model/
    model.py
Views/
    main_view.py

App.py

import sys
from PyQt5.QtWidgets import QApplication

from Model.model import Model
from Controllers.main_controller import MainController
from Views.main_view import MainView


class App(QApplication):
    def __init__(self, sys_argv):
        super().__init__(sys_argv)
        self.model = Model()
        self.main_controller = MainController(self.model)
        self.main_view = MainView(self.model, self.main_controller)


if __name__ == "__main__":
    app = App(sys.argv)
    sys.exit(app.exec_())

model.py

from PyQt5.QtCore import QObject


class Model(QObject):
    def __init__(self):
        super().__init__()
        self.directory = ""

    def get_directory(self):
        return self.directory

    def set_directory(self, directory):
        self.directory = directory

main_view.py

from PyQt5.QtWidgets import QMenu, QSystemTrayIcon, QMainWindow, QFileDialog
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication
from Controllers.recorder import Recorder


class MainView(QMainWindow):

    def __init__(self, model, main_controller):
        super().__init__()

        self._model = model
        self._main_controller = main_controller

        # UI
        icon = QtGui.QIcon("icon24x24.png")
        menu = QMenu()
        start_action = menu.addAction("Start Recording")
        stop_action = menu.addAction("Stop Recording")
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(icon)
        self.tray.setContextMenu(menu)
        self.tray.show()

        start_action.triggered.connect(self.start_app)
        stop_action.triggered.connect(self.stop_app)

        self.recordThread = Recorder()

    def start_app(self):
        directory = QFileDialog.getExistingDirectory(self, "Select Directory")
        self._main_controller.set_directory(directory)
        self.start_thread()

    def start_thread(self):
        self.recordThread.start()

    def stop_app(self):
        self.recordThread.terminate()
        QApplication.instance().quit()
        print("app stopped")

main_controller.py

from PyQt5.QtCore import QObject


class MainController(QObject):

    def __init__(self, model):
        super().__init__()
        self._model = model

    def set_directory(self, directory):
        self._model.set_directory(directory)

recorder.py

import time
from PyQt5.QtCore import QThread, QTimer, pyqtSignal
from Model.model import Model


class Recorder(QThread):

    job_done = pyqtSignal()

    def __init__(self):
        QThread.__init__(self)
        self._model = Model()

    def __del__(self):
        self.wait()

    def run(self):
        while True:
            print("I am the loop")
            print(self._model.get_directory())
            # time.sleep(4 - time.time() % 4)
            QThread.sleep(4)
            print("now is {}".format(time.time()))
        self.job_done.emit()

I've tried using various style, including Qthread, QObject, pyqtsignal according to various tutorial. but nothing works for me. It either just print "I am the loop" then exit. or print

I am the loop
Fatal Python error: could not acquire lock for <_io.BufferedWriter name='<stdout>'> at interpreter shutdown, possibly due to daemon threads

Thread 0x00007f5d1bfff700 (most recent call first):
  File "/App/Controllers/recorder.py", line 20 in run

Current thread 0x00007f5d3458f700 (most recent call first):
Aborted

Thank you

Nothing wrong with your code. The app closed immediately after calling QFileDialog.getExistingDirectory because it's the nature of Qt.

Qt is made to exit when all Windows are closed

The app closed because you no longer have any windows. Your app doesn't have any windows but QSystemTrayIcon() . Setting setQuitOnLastWindowClosed() to False solved the problem.

yourApp.setQuitOnLastWindowClosed(False)

Source

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM