QThread: Destroyed while thread is still running

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Octarock
User
Beiträge: 25
Registriert: Dienstag 3. April 2018, 01:21

Hallo,

ich habe ein Programm, welches ich in mehreren Schritten programmiert habe (jede Seite für sich) und habe nun Probleme beim Zusammenfügen.
Die einzelnen Windows haben für sich funktioniert nur jetzt funktioniert der worker vom Mainwindow nicht richtig. Da ich von PyQt noch recht wenig Ahnung habe, kann es sein, dass der Fehler auch recht offensichtlich ist.
Hier das Mainwindow für sich (funktionierend):

Code: Alles auswählen

import os
import time
from PyQt5 import QtCore, QtWidgets, QtGui



path = "C:/Users/benni/Desktop/"  # given from the first Window
folder_name = "exit_folder_test"  # given from the first Window
source = "test"
extension = "jpg"
folder_finised = "saved"

if not os.path.isdir(os.path.join(path, folder_name)):
    os.makedirs(os.path.join(path, folder_name))


class PictureWorker(QtCore.QObject):
    updateSignal = QtCore.pyqtSignal()

    @QtCore.pyqtSlot()
    def search(self):
        recording = True
        number = 0
        amount = 0
        while recording:
            pictures = []
            for file in os.listdir(os.path.join(path, source)):       # add all files with ".jpg" to the list pictures
                if file.endswith(".{}".format(extension)):
                    pictures.append(file)
            amount_update = len(pictures)
            if amount != amount_update:  # checks if the amount of files in the folder has changed
                amount = amount_update
                for picture_name in pictures:
                    number += 1
                    if 0 < number < 7:
                        time.sleep(1)  # wait until the picture is transferred
                        picture_new_name = "image{}.{}".format(number, extension)
                        os.rename(
                            os.path.join(path, source, picture_name),
                            os.path.join(path, folder_name, picture_new_name),
                        )  # renames all the given pictures and transfer them in a new folder  
                        print(number)
                        self.updateSignal.emit()
                    else:
                        recording = False
                #recording = False


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
        grid_layout = QtWidgets.QGridLayout(widget)
        rows, cols = 2, 3
        self._labels = []
        global image_width, image_height
        image_width, image_height = 600, 400

        for row in range(rows):
            for col in range(cols):
                label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
                label.setFixedSize(image_width, image_height)
                grid_layout.addWidget(label, row, col)
                self._labels.append(label)



        self.ComboBox = QtWidgets.QComboBox(self)
        grid_layout.addWidget(self.ComboBox, 3, 2)



        self.print_button = QtWidgets.QPushButton("print")  # print button
        grid_layout.addWidget(self.print_button, 3, 3)
        self.print_button.clicked[bool].connect(self.print)
    
        self.resize(1920, 1080)

    def update_image(self):
        for i, label in enumerate(self._labels):
            pixmap = QtGui.QPixmap(
                os.path.join(
                    path, folder_name, "image{}.{}".format(i + 1, extension)
                )
            ).scaled(image_width, image_height)
            label.setPixmap(pixmap)
            
            if len(os.listdir(path + folder_name)) == i+1:
                self.ComboBox.addItem("image{}.{}".format(i + 1, extension))

    def print(self):
        print("test")
        

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    worker = PictureWorker()
    worker.updateSignal.connect(w.update_image)
    thread = QtCore.QThread()
    thread.start()
    worker.moveToThread(thread)
    QtCore.QTimer.singleShot(0, worker.search)
    sys.exit(app.exec_())
Und hier das neue Programm zusammengesetzt:

Code: Alles auswählen

import sys
from PyQt5 import QtCore, QtWidgets, QtGui
import time
import os
                       
path = "C:/Users/benni/Desktop/"  
folder_name = "exit_folder_test"  
name_list = "used_names.txt"
source = "test"
extension = "jpg"

if not os.path.isdir(os.path.join(path, folder_name)):
    os.makedirs(os.path.join(path, folder_name))

if not os.path.isfile(os.path.join(path, name_list)):
    used_names = open(path + name_list, "w+")
    used_names.close()
    names =[]
else:
    used_names = open(path + name_list, "r")
    names = used_names.readlines() 
    used_names.close()
    print(names)

#####################################################################################

class PictureWorker(QtCore.QObject):
    updateSignal = QtCore.pyqtSignal()

    @QtCore.pyqtSlot()
    def search(self):
        recording = True
        number = 0
        amount = 0
        while recording:
            pictures = []
            for file in os.listdir(os.path.join(path, source)):       # add all files with ".jpg" to the list pictures
                if file.endswith(".{}".format(extension)):
                    pictures.append(file)
            amount_update = len(pictures)
            if amount != amount_update:                               # checks if the amount of files in the folder has changed
                amount = amount_update
                for picture_name in pictures:
                    number += 1
                    if 0 < number < 7:
                        time.sleep(1)                                 # wait until the picture is transferred
                        picture_new_name = "image{}.{}".format(number, extension)
                        os.rename(
                            os.path.join(path, source, picture_name),
                            os.path.join(path, folder_name, picture_new_name),
                        )                                             # renames all the given pictures and transfer them in a new folder  
                        print(number)
                        self.updateSignal.emit()
                    else:
                        recording = False         

######################################################################################

class MainWindow(QtWidgets.QWidget):

    switch_window = QtCore.pyqtSignal(str)

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.setWindowTitle('Main Window')
        QtWidgets.QWidget.__init__(self)
        grid_layout = QtWidgets.QGridLayout()
        rows, cols = 2, 3
        self._labels = []
        global image_width, image_height
        image_width, image_height = 600, 400

        for row in range(rows):
            for col in range(cols):
                label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
                label.setFixedSize(image_width, image_height)
                grid_layout.addWidget(label, row, col)
                self._labels.append(label)

        self.ComboBox = QtWidgets.QComboBox(self)                    # a combobox to choose the picture which is going to be print
        grid_layout.addWidget(self.ComboBox, 3, 2)

        self.print_button = QtWidgets.QPushButton("print")           # print button
        grid_layout.addWidget(self.print_button, 3, 3)
        self.print_button.clicked[bool].connect(self.print)
    
        self.resize(1920, 1080)

    def update_image(self):                                   
        for i, label in enumerate(self._labels):
            pixmap = QtGui.QPixmap(
                os.path.join(
                    path, folder_name, "image{}.{}".format(i + 1, extension)
                )
            ).scaled(image_width, image_height)
            label.setPixmap(pixmap)
            
            if len(os.listdir(path + folder_name)) == i+1:
                self.ComboBox.addItem("image{}.{}".format(i + 1, extension))

    def print(self):
        print("test")           # the print function is missing right now

        self.switch_window.emit()

###########################################################################################################

class WindowTwo(QtWidgets.QWidget):          

    def __init__(self, text):
        QtWidgets.QWidget.__init__(self)
        self.setWindowTitle('Window Two')

        layout = QtWidgets.QGridLayout()

        self.label = QtWidgets.QLabel("                                     Ihre Auswahl wird 2x gedruckt. Bitte Warten.\nFals Sie die Bilder ebenfalls digital erhalten möchten, können Sie hier Ihre E-Mail hinterlegen.")
        layout.addWidget(self.label,1,2, alignment=QtCore.Qt.AlignCenter)      # says the user that the printer is working
        font = self.label.font()             
        font.setPointSize(17)                   
        self.label.setFont(font)                    
        
        self.lineEdit = QtWidgets.QLineEdit()                                  # email input 
        self.lineEdit.setFixedSize(340,31)
        layout.addWidget(self.lineEdit, 2,2, alignment=QtCore.Qt.AlignCenter)
        font = self.lineEdit.font()             
        font.setPointSize(17)                   
        self.lineEdit.setFont(font)             

        self.finish_button = QtWidgets.QPushButton('Fertig')                   # button when user has finished 
        self.finish_button.clicked.connect(self.finised)                  
        self.finish_button.setFixedSize(75,23)
        layout.addWidget(self.finish_button, 3,3, alignment=QtCore.Qt.AlignBottom)

        self.setLayout(layout)
        self.resize(1920, 1080)

    
    def finished(self):                              # saves the given email
        email = self.lineEdit.text()
        if email != "":
            email_text = open(path + folder_name + "/email.txt", "w+")
            email_text.write(email)
            email_text.close()
            self.close()                             # reset funktion fehlt

###########################################################################################################

class Login(QtWidgets.QWidget):

    switch_window = QtCore.pyqtSignal()

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.setWindowTitle('Fotobox')
        self.resize(1920,1080)

        layout = QtWidgets.QGridLayout()

        self.Button = QtWidgets.QPushButton('Weiter')        
        self.Button.setFixedSize(75,23)
        layout.addWidget(self.Button, 3,3, alignment=QtCore.Qt.AlignRight)
        self.Button.clicked.connect(self.on_click)                           
        
        self.label = QtWidgets.QLabel("Name: ")
        self.label.setFixedSize(120,51)
        layout.addWidget(self.label, 1,1, alignment=QtCore.Qt.AlignRight)
        font1 = self.label.font()                        # line-edit current font
        font1.setPointSize(28)                           # change it's size
        self.label.setFont(font1)                        # set font
            
        self.error_label = QtWidgets.QLabel("")          # the label where the error is shown
        self.error_label.setFixedSize(120,51)
        layout.addWidget(self.error_label, 2,2)
        
        self.lineEdit = QtWidgets.QLineEdit()            # input label for the name of the person/group
        self.lineEdit.setFixedSize(260,31)
        layout.addWidget(self.lineEdit, 1,2)
        font2 = self.lineEdit.font()             
        font2.setPointSize(17)                  
        self.lineEdit.setFont(font2)            
        self.lineEdit.textChanged.connect(self.issue_test)                

        self.setLayout(layout)

    def issue_test(self):                    # tests if the name is used before
        self.issue = False                       
        name = self.lineEdit.text()          # reads input label
        for i in names:                          
            if name == i:                    # if name is already used before: red & bg yellow + issue -> True
                self.lineEdit.setStyleSheet("color: red; background-color: yellow;") 
                self.error_label.setText("Name bereits Vorhanden! \n   Bitte Eingabe ändern.")
                self.error_label.setStyleSheet("color: red;")
                self.issue = True
        if self.issue == False:              # when no problem exists the error message disapearse and the input label is white 
            self.lineEdit.setStyleSheet("color: black; background-color: white;")
            self.issue = False
            self.error_label.setText("")

    def on_click(self):                      # when user click the button to confirm the input
        global namen                             
        name = self.lineEdit.text()
        if  name == "":                      # tests if the input label is empty
            self.lineEdit.setStyleSheet("color: red; background-color: yellow;")
            self.error_label.setText("     Keine Eingabe! \n Bitte Eingabe ändern.")
            self.error_label.setStyleSheet("color: red;")
            self.issue = True
           
        if self.issue == False:                    # only when input is not empty and not used before
            folder_name = name
            os.mkdir(path + folder_name)           # creates a folder for the user
            names.append(folder_name)              # add the name to the list of the used names
            
            used_names = open(path + name_list, "a")
            used_names.write(name + "\n")
            used_names.close()

            self.switch_window.emit()              # switch to the mainwindow

######################################################################################################################################################

class Controller:

    def __init__(self):
        pass

    def show_login(self):
        self.login = Login()
        self.login.switch_window.connect(self.show_main)
        self.login.show()

    def show_main(self):
        self.window = MainWindow()
        self.window.switch_window.connect(self.show_window_two)
        self.login.close()
        self.window.show()
        
        worker = PictureWorker()
        worker.updateSignal.connect(self.window.update_image)
        thread = QtCore.QThread()
        thread.start()
        worker.moveToThread(thread)
        QtCore.QTimer.singleShot(0, worker.search)
        
def show_window_two(self, text):
        self.window_two = WindowTwo(text)
        self.window.close()
        self.window_two.show()
        
def main():
    app = QtWidgets.QApplication(sys.argv)
    controller = Controller()
    controller.show_login()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Das Programm weist noch ein paar andere Fehler auf, die mir teils auch bekannt sind, nur mit Threadverteilung kenne ich mich echt schlecht aus und ich habe keine Idee was ich da falsch mache.
Zum Verständnis: Das Programm frägt zu Beginn nach einem Namen nach welchen dann ein Ordner angelegt wird. Darauf werden alle Bilder (max.6) angezeigt, welche mit der Zeit in den Ordner erscheinen und der User kann nun ein Bild auswählen und drucken.
Bis zum Wechsel zum Mainwindow funktioniert auch alles aber sobald das neue Fenster startet kommt jener Fehler: "QThread: Destroyed while thread is still running" und das Programm stürzt ab.
Freue mich über jede konstruktive Hilfe :)

MFG
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der threading code in Beispiel 1 ist Murks. Du hast zwar einen worker, den du auch zum Thread bewegst - aber searching startest du dann mit einem timer aus dem Hauptthread. Der andere dödelt also vor sich hin ohne was zu machen. Und wenn das Programm sich beendet, läuft der immer noch & meldet den Fehler den du bekommst.

Schreib das also erstmal richtig. Ich und andere habe hier in der Vergangenheit mit den Usern Attalantore und Sophus darüber diskutiert. Da findest du Beispiele wie es richtig geht.

Wenn das steht, kannst du das integrieren.
Octarock
User
Beiträge: 25
Registriert: Dienstag 3. April 2018, 01:21

sorry aber ich finde Sophus überhaut nicht und bei dem anderen finde ich auch keinen Eintrag der auf mein Problem zutrifft!?
Kannst du mir die chats vielleicht verlinken?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Antworten