Почему не работает код с потоками?

Не работает код. Пишет ошибку: QObject::setParent: Cannot set parent, new parent is in a different thread. Я не могу понять в чем проблема. Помогите. Вот код:


Update

У меня задумка в том что когда я что-то копирую в буфер, в окне должна появится кнопка, на которой будет написан текст из буфера. Если я снова что-то новое поместил в буфер, тогда добавляется новая кнопка в окне.

Я хотел поток, который вовремя работы приложения проверял буфер и добавлял кнопки в окно. Может это как-то по другому сделать.


from PyQt5 import QtWidgets, QtCore
import pyperclip

class ProgressCopyThread(QtCore.QThread):
    def __init__(self, MyWindow, parent = None):
        super().__init__(MyWindow)
        self.window = MyWindow

    def run(self):
        buttonlst = self.window.buttonlist
        total = -1

        while True:
            info_copy = pyperclip.paste()
            if info_copy not in buttonlst:

                total += 1
                buttonlst.append(QtWidgets.QPushButton(str(info_copy)))
                self.window.formLayout.addRow(buttonlst[total])



class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent = None):
        QtWidgets.QWidget.__init__(self, parent)
        self.resize(400, 150)

        self.formLayout = QtWidgets.QFormLayout()
        self.groupBox = QtWidgets.QGroupBox()

        self.buttonlist = []

        ProgressCopyThread(MyWindow = self).start()

        self.groupBox.setLayout(self.formLayout)

        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(self.groupBox)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(150)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(scroll)

        self.setLayout(layout)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

Ответы (2 шт):

Автор решения: S. Nick

Я не совсем понимаю что вы хотите сделать, но с потоками так не работают. То что показывается в окне делается только в основном потоке. Дополнительный поток нужен тогда, когда у вас есть тяжелая задача (длительные расчеты, загрузка данных и т.п.), которая заблокирует интерфейс если ее не делать в потоке. Если вас интересует работа с потоками, тогда полистайте посты, об этой теме уже много раз писали.

Т.к. я увидел что вы пишите о кнопках и у вас есть QFormLayout, появился такой пример.

from PyQt5 import QtWidgets, QtCore

'''
import pyperclip

class ProgressCopyThread(QtCore.QThread):
    def __init__(self, MyWindow, parent = None):
        super().__init__(MyWindow)
        self.window = MyWindow

    def run(self):
        buttonlst = self.window.buttonlist
        total = -1

        while True:
            info_copy = pyperclip.paste()
            if info_copy not in buttonlst:

                total += 1
                buttonlst.append(QtWidgets.QPushButton(str(info_copy)))
                self.window.formLayout.addRow(buttonlst[total])
'''


class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent = None):
        QtWidgets.QWidget.__init__(self, parent)
        self.resize(400, 150)

        self.formLayout = QtWidgets.QFormLayout()
        self.groupBox = QtWidgets.QGroupBox()

#        self.buttonlist = []
        for i in range(12):
            lineEdit = QtWidgets.QLineEdit()
            button = QtWidgets.QPushButton(f'Button {i}')
            button.clicked.connect(lambda ch, l=lineEdit: print(l.text()))
            self.formLayout.addRow(button, lineEdit)   

#        ProgressCopyThread(MyWindow = self).start()

        self.groupBox.setLayout(self.formLayout)

        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(self.groupBox)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(150)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(scroll)

        self.setLayout(layout)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

введите сюда описание изображения


Update:

Тогда ваша задача может выглядеть так. Начинайте что-нибудь копировать и наблюдайте что происходит.

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import (QGridLayout, QPushButton, QTextEdit,
                             QApplication, QWidget)
from PyQt5.QtGui import  QClipboard


class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent = None):
        QtWidgets.QWidget.__init__(self, parent)
        self.resize(400, 150)

        self.formLayout = QtWidgets.QFormLayout()
        self.groupBox = QtWidgets.QGroupBox()

        self.groupBox.setLayout(self.formLayout)

        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(self.groupBox)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(150)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(scroll)

        self.setLayout(layout)

        # Мы можем получить доступ к буферу, используя класс QClipboard. 
        # Затем мы можем вывести этот текст. 
        # Мы подключаем буфер обмена к методу, использующему строку:
        QApplication.clipboard().dataChanged.connect(self.clipboardChanged)

        self.i = 1

    # Получить содержимое системного буфера обмена
    def clipboardChanged(self):
        # Затем мы захватываем содержимое, используя:
        text = QApplication.clipboard().text()

        lineEdit = QtWidgets.QLineEdit(text)
        button = QtWidgets.QPushButton(f'Button {self.i}')
        button.clicked.connect(lambda ch, l=lineEdit: print(l.text()))
        self.formLayout.addRow(button, lineEdit) 
        self.i += 1        


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка
Автор решения: SKIP

Не пишу на Python QT, напишу на С++ QT, думаю все будет понятно.


Нет смысла создавать отдельный поток только ради того, чтобы проверять постоянно буфер. В QT есть класс QClipboard (документация) с его сигналом dataChanged. Снизу небольшой пример:

QClipboard *clipboard = QApplication::clipboard();
connect(clipboard, &QClipboard::dataChanged, this, [this, clipboard]()
{
    // тут можно добавлять новые виджеты или что вы там собираетесь делать
    qDebug() << clipboard->text();
});

Не могу проверить работает ли, но нашел тут (тут же стоит почитать про проблему з дубликацией сигнала) тот же код только для Python:

class MainWindow(QTableView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.clipboard = QApplication.clipboard()
        self.clipboard.dataChanged.connect(self.detectClipboardUrl)  

    @pyqtSlot()
    def detectClipboardUrl(self):
        print(self.clipboard.text())
→ Ссылка