Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 like

    class 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.



  • @Qt-Bot05
    I don't know about whatever windows. But you must not do any UI stuff in a thread. I'm not sure you can use all of QLabel/QImage/QPixmap there like you do?



  • I agree with @JonB. It's definitely not a good idea to use QLabel, QImage, and QPixmap in a QThread. 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.


  • Lifetime Qt Champion

    Hi,

    QImage is allowed as it is independent of graphics system unlike QPixmap. See the Mandelbrot example.



  • @GuyInABlueShirt said in Sending QWidgets as a pyqtSignal():

    I agree with @JonB. It's definitely not a good idea to use QLabel, QImage, and QPixmap in a QThread. 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.



  • @Qt-Bot05
    To be clear: further to comments from others above.

    I believe you can use QImage from a non-UI thread. But I believe you must not use either a QPixmap or a QLabel. That's what I mean by

    I'm not sure you can use all of [...]



  • @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?


  • Lifetime Qt Champion

    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.