Python PyQt5 загружает изображение для QPushButton в QThread

#python #pyqt5 #qthread #qpushbutton #qicon

#python #pyqt5 #qthread #qpushbutton #qicon

Вопрос:

Итак, у меня есть этот пользовательский интерфейс, который загружает МНОГО кнопок, все с изображениями, проблема в том, что это занимает много времени, поэтому я попытался поместить его в a QThread , у меня это работало, но разницы в скорости не было, поэтому я попробовал другое решение, но теперь поток не запускается.

Код:

 import sys
import os
from PyQt5 import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtPrintSupport, QtWidgets, uic

# I've tried using a QThread for this, but it still took the same amount of time.
class LoadImageThumbnailshread(QThread):
    def __init__(self, listButtons, listPaths):
        QThread.__init__(self)
        self.listButtons = listButtons
        self.listPaths = listPaths

    def run(self):
        self.process_images_Thread()

    def process_images_Thread(self):
        for i, j in enumerate(self.listButtons):
            j.setIcon(QIcon(self.listPaths[i]))
            j.setIconSize(QSize(150-6, 60-6))


class App(QDialog):

    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 layout - pythonspot.com'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.images_path = []
        self.button_images = []
        self.initUI()

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

        self.createGridLayout()

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.horizontalGroupBox)
        self.setLayout(windowLayout)

        self.show()

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("Grid")
        layout = QGridLayout()
        layout.setColumnStretch(1, 4)
        layout.setColumnStretch(2, 4)

        for i in range(100):
            self.btnImage = QPushButton()
            self.btnImage.setObjectName('btnImage')
            self.images_path.append(os.path.dirname(
                os.path.abspath(__file__))   'view.png')

            # ! I have also tried using Pixmaps with Clickable labels, but it made no diffrence.
            # pixmap = QPixmap(os.path.dirname(os.path.abspath(__file__))   image_locations[i])
            # pixmap = pixmap.scaled(60, 300, Qt.KeepAspectRatio, Qt.FastTransformation)
            # self.btnImage.setPixmap(pixmap)

            # ! Enableing this loads the images, but very slowly
            # self.btnImage.setIcon(QIcon(os.path.dirname(os.path.abspath(__file__))   '/view.png'))
            # self.btnImage.setIconSize(QSize(150-6, 60-6))
            self.button_images.append(self.btnImage)
            layout.addWidget(self.btnImage, i, 0)

        # ! This starts the QThread
        converter = LoadImageThumbnailshread(
            self.button_images, self.images_path)
        converter.start()
        self.horizontalGroupBox.setLayout(layout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

  

Итак, чтобы объяснить больше того, что я хочу, я хочу запустить QThread ПОСЛЕ загрузки пользовательского интерфейса, который QThread загрузит все изображения на каждую из кнопок. Проблема в том, что сейчас он не работает.

Еще одна проблема, с которой я столкнулся, заключается в том, что когда я получил QThread пользовательский интерфейс, он ждал его завершения, а затем все внезапно появилось. Я хочу QThread , чтобы оно было полностью отдельным и красивым, чтобы все изображения загружались одно за другим, если хотите.

Ответ №1:

Потоки не служат для ускорения какой-либо задачи!!!но для выполнения задач, которые не блокируют какой-либо поток. Учитывая вышесказанное, если у меня есть n «задач», и каждая из них выполняется в «n» потоках, тогда общее время выполнения составит 1 задачу, то есть ни одна задача не ускоряется, но задачи перераспределяются. В заключение: если вы хотите ускорить задачу, то threads не является опцией по умолчанию, поскольку это зависит от приложения.

С другой стороны, загрузка изображения не занимает много времени, но есть много задач, которые занимают мало времени, что в целом эквивалентно задаче, которая занимает много времени, и, к сожалению, эта задача не может быть выполнена в другом потоке или процессе, поскольку элементы графического интерфейса не являются потокобезопасными.

Обходной путь заключается в том, что загрузка выполняется небольшой группой из n элементов каждые T секунд, поэтому общая задача будет распределена, и пользователь не будет наблюдать никакого эффекта задержки.

 import os
import sys

from PyQt5.QtCore import QSize, QTimer
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
    QApplication,
    QDialog,
    QGridLayout,
    QGroupBox,
    QPushButton,
    QVBoxLayout,
)


class App(QDialog):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 layout - pythonspot.com"
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.images_path = []
        self.button_images = []
        self.initUI()

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

        self.createGridLayout()

        windowLayout = QVBoxLayout(self)
        windowLayout.addWidget(self.horizontalGroupBox)

        self._iter = iter(range(100))
        self._timer = QTimer(interval=10, timeout=self.lazy_loading)
        self._timer.start()

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("Grid")
        self.grid_layout = QGridLayout()
        self.grid_layout.setColumnStretch(1, 4)
        self.grid_layout.setColumnStretch(2, 4)
        self.horizontalGroupBox.setLayout(self.grid_layout)

    def lazy_loading(self):
        try:
            i = next(self._iter)
        except StopIteration:
            self._timer.stop()
        else:
            btn = QPushButton()
            image_path = os.path.join(os.path.abspath(__file__), "view.png")
            btn.setIcon(QIcon(image_path))
            btn.setIconSize(QSize(150 - 6, 60 - 6))
            self.grid_layout.addWidget(btn, i, 0)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())
  

Комментарии:

1. Это работает хорошо и останавливает мою программу от «зависания». Но я просто хочу воспользоваться этой возможностью, чтобы спросить. Хотя этот подход работает, не могу ли я использовать стандарт QThread и выполнить эту задачу «после» загрузки всего пользовательского интерфейса?

2. @JareBear Мой ответ ясен: графический интерфейс не является потокобезопасным

3. Хорошо, большое вам спасибо! Иногда меня просто расстраивает, когда я хочу использовать QThread с графическим интерфейсом, но это никогда не получается: ‘D