Почему не срабатывает функция on_finish он выходит с ошибкой?

class SearchGifThread(QThread):
    SITE_URL = 'https://api.giphy.com/v1'
    API_KEY = '57RLnwraja3OJRI7mSQi7pbzcPMkaYe5'

    def __init__(self, name_gif, signal, on_finish, on_start):
        QThread.__init__(self)

        self.name_gif = name_gif
        self.signal = signal
        self.on_finish = on_finish
        self.on_start = on_start

    def get_gif(self, index):
        try:
            response = requests.get(
                f'{SearchGifThread.SITE_URL}/gifs/search?api_key={SearchGifThread.API_KEY}&q={self.name_gif}')
            if response.status_code == 200:
                data = json.loads(response.content.decode('utf-8'))['data']
                if data:
                    gif = data[index]['images']['fixed_width']['url']
                    image_response = requests.get(gif, stream=True)
                    if image_response.status_code == 200:
                        with open(f"./temp/img{index}.gif", 'wb') as f:
                            image_response.raw.decode_content = True
                            f.write(image_response.content)
                    data = {'row': index // 3, 'col': index % 3, 'error': None, 'index': index}
                    return data
                else:
                    data = {'error': 1}
                    return data

        except Exception as err:
            print(err)

    def run(self):
        self.on_start()
        for index_gif in range(20):
            data = self.get_gif(index_gif)
            self.signal.emit(data)
            if data['error']:
                return
        SearchGifThread.counter = 0
        time.sleep(5)
        self.on_finish()


class Window(QMainWindow):
    add_gif_signal = pyqtSignal(dict)

    def __init__(self):
        super().__init__()

        self.width = self.size().width()
        self.height = self.size().height()
        self.left = 200
        self.top = 300
        self.title = 'Gif Manager'

        self.init_window()
        self.init_ui()
        self.show()

    def init_window(self):
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setWindowTitle(self.title)

    def init_ui(self):
        root_layout = QVBoxLayout()
        gif_data_layout = QHBoxLayout()
        gif_data_layout.setAlignment(Qt.AlignTop)
        self.gif_data_frame = QFrame()
        self.gif_edit = QLineEdit()
        regex = QRegExp('[a-z-A-Z]+')
        validator = QRegExpValidator(regex)
        self.gif_edit.setValidator(validator)
        self.gif_edit.setPlaceholderText('Enter name gif')
        gif_search_button = QPushButton('Search gif')
        gif_search_button.clicked.connect(self.search_gif)
        gif_data_layout.addWidget(self.gif_edit)
        gif_data_layout.addWidget(gif_search_button)
        self.gif_data_frame.setLayout(gif_data_layout)
        root_layout.addWidget(self.gif_data_frame)
        self.info_label = QLabel('Something went wrong. Check that the request was made correctly.')
        self.info_label.setAlignment(Qt.AlignHCenter)
        self.info_label.hide()
        root_layout.addWidget(self.info_label)
        self.scroll = QScrollArea()
        self.scroll.setStyleSheet('background: rgba(255, 255, 255, 30%);')
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        scrollWidget = QWidget()
        self.gifs_layout = QGridLayout()
        scrollWidget.setLayout(self.gifs_layout)
        self.scroll.setWidget(scrollWidget)
        root_layout.addWidget(self.scroll)
        root_widget = QWidget()
        root_widget.setLayout(root_layout)
        self.setCentralWidget(root_widget)

    def on_finish(self):
        try:
            self.gif_data_frame.show()
        except Exception as err:
            print(err)

    def on_start(self):
        self.info_label.hide()
        self.scroll.show()
        self.gif_data_frame.hide()

    def search_gif(self):
        name_gif = self.gif_edit.text()
        self.search_gif_thread = SearchGifThread(name_gif, self.add_gif_signal, self.on_finish, self.on_start)
        for c in reversed(range(self.gifs_layout.count())):
            self.gifs_layout.itemAt(c).widget().setParent(None)
        self.add_gif_signal.connect(self.add_gif)
        self.search_gif_thread.start()

    def add_gif(self, data):
        if not data['error']:
            row, col, error, index = data.values()
            self.movie = QMovie(f'./temp/img{index}.gif')
            self.movie.setSpeed(200)
            label_gif = QLabel()
            label_gif.setMovie(self.movie)
            self.movie.start()
            self.gifs_layout.addWidget(label_gif, row, col)
            label_gif.show()
        else:
            self.gif_data_frame.show()
            self.scroll.hide()
            self.info_label.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())

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

Автор решения: gil9red

Плохая идея из одного потока вызывать метод объекта, находящегося в другом потоке :) Для безопасной связи между потоками используйте механизм сигналов-слотов. А то, что у вас сделано это реализация через callback

Например, у QThread уже есть сигналы started и finished, подключите их к методам on_start и on_finish.

Немного подправил код:

class SearchGifThread(QThread):
    SITE_URL = 'https://api.giphy.com/v1'
    API_KEY = '57RLnwraja3OJRI7mSQi7pbzcPMkaYe5'

    about_add_gif = pyqtSignal(dict)            # <<<<<<<<<

    def __init__(self, name_gif):               # <<<<<<<<<
        super().__init__()

        self.name_gif = name_gif

    def get_gif(self, index):
        try:
            response = requests.get(
                f'{SearchGifThread.SITE_URL}/gifs/search?api_key={SearchGifThread.API_KEY}&q={self.name_gif}')
            if response.status_code == 200:
                data = json.loads(response.content.decode('utf-8'))['data']
                if data:
                    gif = data[index]['images']['fixed_width']['url']
                    image_response = requests.get(gif, stream=True)
                    if image_response.status_code == 200:
                        with open(f"./temp/img{index}.gif", 'wb') as f:
                            image_response.raw.decode_content = True
                            f.write(image_response.content)
                    data = {'row': index // 3, 'col': index % 3, 'error': None, 'index': index}
                    return data
                else:
                    data = {'error': 1}
                    return data

        except Exception as err:
            print(err)

    def run(self):
        for index_gif in range(20):
            data = self.get_gif(index_gif)
            self.about_add_gif.emit(data)               # <<<<<<<<<
            if data['error']:
                return
        SearchGifThread.counter = 0
        time.sleep(5)


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.width = self.size().width()
        self.height = self.size().height()
        self.left = 200
        self.top = 300
        self.title = 'Gif Manager'

        self.init_window()
        self.init_ui()
        self.show()

    def init_window(self):
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setWindowTitle(self.title)

    def init_ui(self):
        root_layout = QVBoxLayout()
        gif_data_layout = QHBoxLayout()
        gif_data_layout.setAlignment(Qt.AlignTop)
        self.gif_data_frame = QFrame()
        self.gif_edit = QLineEdit()
        regex = QRegExp('[a-z-A-Z]+')
        validator = QRegExpValidator(regex)
        self.gif_edit.setValidator(validator)
        self.gif_edit.setPlaceholderText('Enter name gif')
        gif_search_button = QPushButton('Search gif')
        gif_search_button.clicked.connect(self.search_gif)
        gif_data_layout.addWidget(self.gif_edit)
        gif_data_layout.addWidget(gif_search_button)
        self.gif_data_frame.setLayout(gif_data_layout)
        root_layout.addWidget(self.gif_data_frame)
        self.info_label = QLabel('Something went wrong. Check that the request was made correctly.')
        self.info_label.setAlignment(Qt.AlignHCenter)
        self.info_label.hide()
        root_layout.addWidget(self.info_label)
        self.scroll = QScrollArea()
        self.scroll.setStyleSheet('background: rgba(255, 255, 255, 30%);')
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        scrollWidget = QWidget()
        self.gifs_layout = QGridLayout()
        scrollWidget.setLayout(self.gifs_layout)
        self.scroll.setWidget(scrollWidget)
        root_layout.addWidget(self.scroll)
        root_widget = QWidget()
        root_widget.setLayout(root_layout)
        self.setCentralWidget(root_widget)

    def on_finish(self):
        try:
            self.gif_data_frame.show()
        except Exception as err:
            print(err)

    def on_start(self):
        self.info_label.hide()
        self.scroll.show()
        self.gif_data_frame.hide()

    def search_gif(self):
        name_gif = self.gif_edit.text()

        # NOTE: Поток, в принципе, можно только в конструкторе класса 
        # создавать (с указанием сигналов), а тут передавать ему name_gif и вызывать его
        self.search_gif_thread = SearchGifThread(name_gif)
        self.search_gif_thread.started.connect(self.on_start)               # <<<<<<<<<
        self.search_gif_thread.finished.connect(self.on_finish)             # <<<<<<<<<
        self.search_gif_thread.about_add_gif.connect(self.add_gif)          # <<<<<<<<<

        for c in reversed(range(self.gifs_layout.count())):
            self.gifs_layout.itemAt(c).widget().setParent(None)

        self.search_gif_thread.start()

    def add_gif(self, data):
        if not data['error']:
            row, col, error, index = data.values()
            self.movie = QMovie(f'./temp/img{index}.gif')
            self.movie.setSpeed(200)
            label_gif = QLabel()
            label_gif.setMovie(self.movie)
            self.movie.start()
            self.gifs_layout.addWidget(label_gif, row, col)
            label_gif.show()
        else:
            self.gif_data_frame.show()
            self.scroll.hide()
            self.info_label.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())

Результат:

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

→ Ссылка