首页 > 解决方案 > 如何使用回调函数更改图标?

问题描述

我的程序允许我将放置文件拖放到要上传到存储域的框中。现在我有了它,所以会出现一个进度条,并实际计算每个文件何时使用回调函数完成。但是,我的程序的其余部分说,一旦每个文件开始上传,它就完成了,而不是完成。

我的问题是如何在每个文件上传完成后更改图像的图标。

storageDomain、awsID 和 awsSecret 是 boto3 变量

import sys, os, threading, logging, math, time, re
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon


class ListBoxWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.resize(600, 600)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
            icon = QIcon('loaded.png')
            for url in event.mimeData().urls():
                if url.isLocalFile():
                    rootPath = url.toString()[8:]
                    if os.path.isfile(rootPath):
                        address = rootPath
                        self.addItem(QListWidgetItem(icon, address))
                    else:
                        for path, subdirs, files in os.walk(rootPath):
                            for file in files:
                                address = str(url.toLocalFile())
                                path = path.replace("\\", "/")
                                directory_name = path.replace(rootPath, "")
                                displayName = directory_name + '/' + file
                                address += displayName
                                self.addItem(QListWidgetItem(icon, address))

        else:
            event.ignore()


class AppDemo(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(1200, 600)

        self.listbox_view = ListBoxWidget(self)

        self.btn = QPushButton('Get Value', self)
        self.btn.setGeometry(850, 400, 200, 50)
        self.btn.clicked.connect(self.startUpload)

        self.qs3 = S3Worker(self.storageDomain, self.awsID, self.awsSecret)

        self.percentageBar = QtWidgets.QProgressBar()
        self.percentageBar.setGeometry(500, 300, 400, 30)
        self.qs3.started.connect(lambda: self.uploadBtn.setEnabled(False))
        self.qs3.finished.connect(lambda: self.uploadBtn.setEnabled(True))
        self.qs3.percentageChanged.connect(self.percentageBar.setValue)

    def startUpload(self):
        firstFile = QListWidgetItem(self.listbox_view.item(0).text())
        firstLen = len(firstFile.text().split("/"))
        for i in range(self.count):
            path = QListWidgetItem(self.listbox_view.item(i).text())
            splitName = path.text().split("/")
            if len(splitName) > firstLen:
                displayName = ""
                difference = len(splitName) - firstLen
                if os.path.exists(path.text()):
                    displayName += self.user + "/" + self.toolID
                    for dif in range(difference + 1, 0, -1):
                        displayName += "/" + self.timeStamp + "/" + splitName[len(splitName) - dif]
                    self.qs3.upload(path.text(), self.bucketName, displayName)
                    self.listbox_view.item(i).setIcon(QIcon("uploaded.png"))
            else:
                if os.path.exists(path.text()):
                    displayName = self.user + "/" + self.toolID + "/" + self.timeStamp + "/" + splitName[
                        len(splitName) - 1]
                    self.qs3.upload(path.text(), self.bucketName, displayName)
                    self.listbox_view.item(i).setIcon(QIcon("uploaded.png"))

        else:
            print("No File inputted")

class S3Worker(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    percentageChanged = QtCore.pyqtSignal(int)

    def __init__(self, domain, awsID, awsSecret, parent=None):
        super().__init__(parent)
        self.domain = domain
        self.awsID = awsID
        self.awsSecret = awsSecret
        self._s3 = boto3.client("s3", endpoint_url=self.domain,
                                aws_access_key_id=self.awsID,
                                aws_secret_access_key=self.awsSecret)

    @property
    def s3(self):
        return self._s3

    def upload(self, filename, bucketname, objectname):
        self._size = float(os.path.getsize(filename))
        self._seen_so_far = 0
        threading.Thread(
            target=self._execute, args=(filename, bucketname, objectname), daemon=True
        ).start()

    def _execute(self, fileName, bucketName, objectName):
        self.started.emit()
        self.s3.upload_file(fileName, bucketName, objectName, Callback=self._callback)
        self.finished.emit()

    def _callback(self, bytes_amount):
        self._seen_so_far += bytes_amount
        percentage = (self._seen_so_far / self._size) * 100
        if percentage > 100:
            self.percentageChanged.emit(100)
        else:
            self.percentageChanged.emit(math.floor(percentage))

        #I fell like I need to change the icons in here


if __name__ == '__main__':
    app = QApplication(sys.argv)

    demo = AppDemo()
    demo.show()

    sys.exit(app.exec_())

标签: pythonpyqtpyqt5boto3boto

解决方案


您说您的代码有效,但我看到许多不一致之处(除了未创建哪些变量),因此我的解决方案将修改各个方面:

  • 如果你要上传 n 个文件,那么你必须有 n 个 QProgressBar。
  • 我不明白您在拖放中过滤的逻辑,所以简单地说,在这里存储了完整的路径,并通过委托建立了文件的名称。
  • 与 displayName 相同。

诀窍也是建立一个标识符,以便在加载结束时建立图标。

from functools import partial
import math
import os
import sys
import threading
import uuid

import boto3

from PyQt5.QtCore import (
    Qt,
    QUrl,
    QObject,
    pyqtSignal,
    QFileInfo,
    QDirIterator,
    QDir,
    pyqtSlot,
)
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QListWidget,
    QPushButton,
    QProgressBar,
    QListWidgetItem,
    QStyledItemDelegate,
)

PathRole = Qt.UserRole + 1000
IdentifierRole = Qt.UserRole + 1001


class NameDelegate(QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        option.text = QFileInfo(index.data(PathRole)).fileName()


class ListBoxWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.resize(600, 600)

        delegate = NameDelegate(self)
        self.setItemDelegate(delegate)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
            icon = QIcon("loaded.png")
            for url in event.mimeData().urls():
                if url.isLocalFile():
                    fi = QFileInfo(url.toLocalFile())
                    if fi.isDir():
                        it = QDirIterator(
                            fi.fileName(), QDir.Files, QDirIterator.Subdirectories,
                        )
                        while it.hasNext():
                            item = QListWidgetItem()
                            item.setData(PathRole, it.next())
                            self.addItem(item)
                    elif fi.isFile():
                        item = QListWidgetItem()
                        item.setData(PathRole, url.toLocalFile())
                        self.addItem(item)

        else:
            event.ignore()


class AppDemo(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(1200, 600)

        self.listbox_view = ListBoxWidget(self)

        self.btn = QPushButton("Get Value", self)
        self.btn.setGeometry(850, 400, 200, 50)
        self.btn.clicked.connect(self.startUpload)

        self.qs3 = S3Worker("storageDomain", "awsID", "awsSecret")

        self.qs3.started.connect(self.handle_started)
        self.qs3.finished.connect(self.handle_finished)
        self.qs3.percentageChanged.connect(self.handle_percentageChanged)

        self.percentageBar_container = dict()

    def startUpload(self):
        for i in range(self.listbox_view.count()):
            item = self.listbox_view.item(i)
            filename = item.data(PathRole)
            identifier = uuid.uuid4()
            item.setData(IdentifierRole, identifier)

            # this code is for test
            displayname = str(identifier)
            bucketName = "bucketName"

            self.qs3.upload(filename, bucketName, displayname, identifier)

        if self.listbox_view.count() == 0:
            print("No File inputted")

    @pyqtSlot(uuid.UUID)
    def handle_started(self, identifier):
        if not self.percentageBar_container:
            self.btn.setEnabled(False)
        percentageBar = QProgressBar()
        percentageBar.setGeometry(500, 300, 400, 30)
        percentageBar.show()
        self.percentageBar_container[identifier] = percentageBar

    @pyqtSlot(uuid.UUID)
    def handle_finished(self, identifier):
        if self.percentageBar_container.get(identifier):
            del self.percentageBar_container[identifier]
            model = self.listbox_view.model()
            indexes = model.match(
                model.index(0, 0), IdentifierRole, identifier, -1, Qt.MatchExactly
            )
            for index in indexes:
                item = self.listbox_view.itemFromIndex(index)
                item.setIcon(QIcon("uploaded.png"))
        if not self.percentageBar_container:
            self.btn.setEnabled(True)

    @pyqtSlot(uuid.UUID, int)
    def handle_percentageChanged(self, identifier, percentage):
        percentageBar = self.percentageBar_container.get(identifier)
        if percentageBar is not None:
            percentageBar.setValue(percentage)


class S3Worker(QObject):
    started = pyqtSignal(uuid.UUID)
    finished = pyqtSignal(uuid.UUID)
    percentageChanged = pyqtSignal(uuid.UUID, int)

    def __init__(self, domain, awsID, awsSecret, parent=None):
        super().__init__(parent)
        self.domain = domain
        self.awsID = awsID
        self.awsSecret = awsSecret
        self._s3 = boto3.client(
            "s3",
            endpoint_url=self.domain,
            aws_access_key_id=self.awsID,
            aws_secret_access_key=self.awsSecret,
        )
        self._seen_so_far = dict()

    @property
    def s3(self):
        return self._s3

    def upload(self, filename, bucketname, objectname, identifier):
        self._size = float(os.path.getsize(filename))
        self._seen_so_far[identifier] = 0
        threading.Thread(
            target=self._execute,
            args=(filename, bucketname, objectname, identifier),
            daemon=True,
        ).start()

    def _execute(self, fileName, bucketName, objectName, identifier):
        self.started.emit(identifier)
        self.s3.upload_file(
            fileName,
            bucketName,
            objectName,
            Callback=partial(self._callback, identifier),
        )
        self.finished.emit(identifier)

    def _callback(self, identifier, bytes_amount):
        self._seen_so_far[identifier] += bytes_amount
        percentage = (self._seen_so_far / self._size) * 100
        if percentage > 100:
            self.percentageChanged.emit(identifier, 100)
        else:
            self.percentageChanged.emit(identifier, math.floor(percentage))


if __name__ == "__main__":
    app = QApplication(sys.argv)

    demo = AppDemo()
    demo.show()

    sys.exit(app.exec_())

推荐阅读