Почему не срабатывает функция 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_())
Результат:
