python - 如何使用回调函数更改图标?
问题描述
我的程序允许我将放置文件拖放到要上传到存储域的框中。现在我有了它,所以会出现一个进度条,并实际计算每个文件何时使用回调函数完成。但是,我的程序的其余部分说,一旦每个文件开始上传,它就完成了,而不是完成。
我的问题是如何在每个文件上传完成后更改图像的图标。
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_())
解决方案
您说您的代码有效,但我看到许多不一致之处(除了未创建哪些变量),因此我的解决方案将修改各个方面:
- 如果你要上传 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_())
推荐阅读
- amazon-web-services - Airflow 仅从 SQS 队列中获取一条消息
- python - 数据框连接表和 groupby 没有给出预期的输出
- java - 如何在 jenkins 作业中运行 main (mvn java)
- amazon-web-services - 过滤器不适用于 int 类型的属性、带有 GSI 的 DynamoDB
- java - Selenium 错误 (Java) - 检测到框架,但在尝试切换到框架时提示 noSuchFrameException
- c# - DateTime.Now 连续多次返回相同的时间
- python - tkinter GUI 文本框未显示类似于终端输出的所有结果
- python - 尝试导入 Keras pad_sequences 和 eli5 时出错
- android - 如何在 tablayout 中以不同方式设置选项卡文本颜色
- javascript - 使用订阅的 Angular 方法装饰器错误处理