Sending QWidgets as a pyqtSignal()
-
I'm trying to load multiple images into labels, then put them inside a table. The screen keeps freezing so
I decided to load these images from a different thread but I run into a problem.When I try to set the immitted label as the table's cell widget it open's another window.
Here's an example of what my current code looks likeclass Thread(QThread): item_signal = pyqtSignal(int, object) row_signal = pyqtSignal(int) def __init__(self, image_links): super(Thread, self).__init__() self.image_links = image_links def run(self): self.row_signal.emit(len(self.image_links)) for row, i in enumerate(self.image_links): image_holder = QLabel() image = QImage() image.loadFromData(requests.get(i).content) image_holder.setPixmap(QPixmap(image)) self.item_signal.emit(row, image_holder) class Window(QWidget): def __init__(self): super(Window, self).__init__() self.table = QTableWidget() self.worker = Thread([link1, link2..]) self.worker.row_signal.connect(self.table.setRowCount) self.worker.item_signal.connect(self.func) self.worker.start() def func(self, row, widget): self.table.setCellWidget(row, 0, widget)
This opens another window with the widget.
I know I can just set the cellwidget inside the thread but I was thinking of making the thread reusable. -
I'm trying to load multiple images into labels, then put them inside a table. The screen keeps freezing so
I decided to load these images from a different thread but I run into a problem.When I try to set the immitted label as the table's cell widget it open's another window.
Here's an example of what my current code looks likeclass Thread(QThread): item_signal = pyqtSignal(int, object) row_signal = pyqtSignal(int) def __init__(self, image_links): super(Thread, self).__init__() self.image_links = image_links def run(self): self.row_signal.emit(len(self.image_links)) for row, i in enumerate(self.image_links): image_holder = QLabel() image = QImage() image.loadFromData(requests.get(i).content) image_holder.setPixmap(QPixmap(image)) self.item_signal.emit(row, image_holder) class Window(QWidget): def __init__(self): super(Window, self).__init__() self.table = QTableWidget() self.worker = Thread([link1, link2..]) self.worker.row_signal.connect(self.table.setRowCount) self.worker.item_signal.connect(self.func) self.worker.start() def func(self, row, widget): self.table.setCellWidget(row, 0, widget)
This opens another window with the widget.
I know I can just set the cellwidget inside the thread but I was thinking of making the thread reusable. -
I agree with @JonB. It's definitely not a good idea to use
QLabel
,QImage
, andQPixmap
in aQThread
. Your best bet is to create a worker class that does all the work you need, and have that emit some signal and method (It's using PySide6 though, I'm sure it won't be too hard to covert it to pyqt5).from PySide6.QtCore import QObject, QThread, Signal from PySide6.QtWidgets import QLabel, QWidget class Worker(QObject): finished = Signal() def __init__(self, image_links, parent=None): super(Worker, self).__init__(parent) self.image_links = image_links self.widgets = [] def run(self): for i in self.image_links: # Do work here.. holder = QLabel() self.widgets.append(holder) self.finished.emit() def get_widgets(self): return self.widgets class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) self.thread = QThread() self.worker = Worker(['link1', 'link2', 'etc']) self.worker.moveToThread(self.thread) self._connect_signals() self.thread.start() def _connect_signals(self): self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.do_stuff_with_widgets) def do_stuff_with_widgets(self): for widget in self.worker.get_widgets(): # Do stuff with widgets pass
I used this for help: https://realpython.com/python-pyqt-qthread/
This should let you do what you want to do.
-
Hi,
QImage is allowed as it is independent of graphics system unlike QPixmap. See the Mandelbrot example.
-
I agree with @JonB. It's definitely not a good idea to use
QLabel
,QImage
, andQPixmap
in aQThread
. Your best bet is to create a worker class that does all the work you need, and have that emit some signal and method (It's using PySide6 though, I'm sure it won't be too hard to covert it to pyqt5).from PySide6.QtCore import QObject, QThread, Signal from PySide6.QtWidgets import QLabel, QWidget class Worker(QObject): finished = Signal() def __init__(self, image_links, parent=None): super(Worker, self).__init__(parent) self.image_links = image_links self.widgets = [] def run(self): for i in self.image_links: # Do work here.. holder = QLabel() self.widgets.append(holder) self.finished.emit() def get_widgets(self): return self.widgets class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) self.thread = QThread() self.worker = Worker(['link1', 'link2', 'etc']) self.worker.moveToThread(self.thread) self._connect_signals() self.thread.start() def _connect_signals(self): self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.do_stuff_with_widgets) def do_stuff_with_widgets(self): for widget in self.worker.get_widgets(): # Do stuff with widgets pass
I used this for help: https://realpython.com/python-pyqt-qthread/
This should let you do what you want to do.
@GuyInABlueShirt said in Sending QWidgets as a pyqtSignal():
I agree with @JonB. It's definitely not a good idea to use
QLabel
,QImage
, andQPixmap
in aQThread
. Your best bet is to create a worker class that does all the work you need, and have that emit some signal and method (It's using PySide6 though, I'm sure it won't be too hard to covert it to pyqt5).This technique doesn't resolve the QWidget access from a non-gui thread.
-
I'm trying to load multiple images into labels, then put them inside a table. The screen keeps freezing so
I decided to load these images from a different thread but I run into a problem.When I try to set the immitted label as the table's cell widget it open's another window.
Here's an example of what my current code looks likeclass Thread(QThread): item_signal = pyqtSignal(int, object) row_signal = pyqtSignal(int) def __init__(self, image_links): super(Thread, self).__init__() self.image_links = image_links def run(self): self.row_signal.emit(len(self.image_links)) for row, i in enumerate(self.image_links): image_holder = QLabel() image = QImage() image.loadFromData(requests.get(i).content) image_holder.setPixmap(QPixmap(image)) self.item_signal.emit(row, image_holder) class Window(QWidget): def __init__(self): super(Window, self).__init__() self.table = QTableWidget() self.worker = Thread([link1, link2..]) self.worker.row_signal.connect(self.table.setRowCount) self.worker.item_signal.connect(self.func) self.worker.start() def func(self, row, widget): self.table.setCellWidget(row, 0, widget)
This opens another window with the widget.
I know I can just set the cellwidget inside the thread but I was thinking of making the thread reusable. -
I agree with @JonB. It's definitely not a good idea to use
QLabel
,QImage
, andQPixmap
in aQThread
. Your best bet is to create a worker class that does all the work you need, and have that emit some signal and method (It's using PySide6 though, I'm sure it won't be too hard to covert it to pyqt5).from PySide6.QtCore import QObject, QThread, Signal from PySide6.QtWidgets import QLabel, QWidget class Worker(QObject): finished = Signal() def __init__(self, image_links, parent=None): super(Worker, self).__init__(parent) self.image_links = image_links self.widgets = [] def run(self): for i in self.image_links: # Do work here.. holder = QLabel() self.widgets.append(holder) self.finished.emit() def get_widgets(self): return self.widgets class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) self.thread = QThread() self.worker = Worker(['link1', 'link2', 'etc']) self.worker.moveToThread(self.thread) self._connect_signals() self.thread.start() def _connect_signals(self): self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.do_stuff_with_widgets) def do_stuff_with_widgets(self): for widget in self.worker.get_widgets(): # Do stuff with widgets pass
I used this for help: https://realpython.com/python-pyqt-qthread/
This should let you do what you want to do.
@GuyInABlueShirt When I use the setCellWidget it still opens another window with the QLabel in it. Is what I'm trying to do impossible? Or maybe I should use a python threading library?
-
Do not use setCellWidget.
Generate your images and store them in your model. Either use the decoration role to show them or a dedicated item delegate.