Add upload progress display in developer, bugfix on file download in developer

This commit is contained in:
2021-04-26 12:07:47 +02:00
parent 8a2cd9f304
commit 4eec6306a9
4 changed files with 269 additions and 38 deletions

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'backgroundworker.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_diag_backgroundworker(object):
def setupUi(self, diag_backgroundworker):
diag_backgroundworker.setObjectName("diag_backgroundworker")
diag_backgroundworker.resize(418, 97)
diag_backgroundworker.setModal(True)
self.verticalLayout = QtWidgets.QVBoxLayout(diag_backgroundworker)
self.verticalLayout.setObjectName("verticalLayout")
self.lbl_status = QtWidgets.QLabel(diag_backgroundworker)
self.lbl_status.setText("Status message...")
self.lbl_status.setObjectName("lbl_status")
self.verticalLayout.addWidget(self.lbl_status)
self.pgb_status = QtWidgets.QProgressBar(diag_backgroundworker)
self.pgb_status.setMinimumSize(QtCore.QSize(400, 0))
self.pgb_status.setObjectName("pgb_status")
self.verticalLayout.addWidget(self.pgb_status)
self.btn_box = QtWidgets.QDialogButtonBox(diag_backgroundworker)
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel)
self.btn_box.setCenterButtons(True)
self.btn_box.setObjectName("btn_box")
self.verticalLayout.addWidget(self.btn_box)
self.retranslateUi(diag_backgroundworker)
QtCore.QMetaObject.connectSlotsByName(diag_backgroundworker)
def retranslateUi(self, diag_backgroundworker):
_translate = QtCore.QCoreApplication.translate
diag_backgroundworker.setWindowTitle(_translate("diag_backgroundworker", "File transfer..."))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
diag_backgroundworker = QtWidgets.QDialog()
ui = Ui_diag_backgroundworker()
ui.setupUi(diag_backgroundworker)
diag_backgroundworker.show()
sys.exit(app.exec_())

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>diag_backgroundworker</class>
<widget class="QDialog" name="diag_backgroundworker">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>418</width>
<height>97</height>
</rect>
</property>
<property name="windowTitle">
<string>File transfer...</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lbl_status">
<property name="text">
<string notr="true">Status message...</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="pgb_status">
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="btn_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
"""File transfer system to handle QThreads."""
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2021 Sven Sager"
__license__ = "GPLv3"
from logging import getLogger
from PyQt5 import QtCore, QtGui, QtWidgets
from ui.backgroundworker_ui import Ui_diag_backgroundworker
log = getLogger()
class BackgroundWorker(QtCore.QThread):
steps_todo = QtCore.pyqtSignal(int)
steps_done = QtCore.pyqtSignal(int)
status_message = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(BackgroundWorker, self).__init__(parent)
def check_cancel(self) -> bool:
"""
Check for interruption of thread and show message
:return: True, if interruption was requested
"""
if self.isInterruptionRequested():
self.status_message.emit(self.tr("User requested cancellation..."))
self.msleep(750)
return True
return False
def exec_dialog(self) -> int:
diag = WorkerDialog(self, self.parent())
rc = diag.exec()
diag.deleteLater()
return rc
def wait_interruptable(self, seconds=-1) -> None:
"""Save function to wait and get the cancel buttons."""
counter = seconds * 4
while counter != 0:
counter -= 1
self.msleep(250)
if self._check_cancel():
break
def run(self) -> None:
"""Worker thread to import pictures from camera."""
log.debug("BackgroundWorker.run")
self.status_message.emit("Started dummy thread...")
self.wait_interruptable(5)
self.status_message.emit("Completed dummy thread.")
self._save_wait(3)
class WorkerDialog(QtWidgets.QDialog, Ui_diag_backgroundworker):
def __init__(self, worker_thread: BackgroundWorker, parent=None):
"""
Base of dialog to show progress from a background thread.
:param worker_thread: Thread with the logic work to do
:param parent: QtWidget
"""
super(WorkerDialog, self).__init__(parent)
self.setupUi(self)
self._canceled = False
self._th = worker_thread
self._th.finished.connect(self.on_th_finished)
self._th.steps_todo.connect(self.pgb_status.setMaximum)
self._th.steps_done.connect(self.pgb_status.setValue)
self._th.status_message.connect(self.lbl_status.setText)
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
a0.ignore()
def exec(self) -> int:
self._th.start()
return super(WorkerDialog, self).exec()
@QtCore.pyqtSlot()
def on_th_finished(self) -> None:
"""Check the result of import thread."""
if self._canceled:
self.reject()
else:
self.accept()
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
def on_btn_box_clicked(self, button: QtWidgets.QAbstractButton) -> None:
"""Control buttons for dialog."""
role = self.btn_box.buttonRole(button)
log.debug("WorkerDialog.on_btn_box_clicked({0})".format(role))
if role == QtWidgets.QDialogButtonBox.RejectRole:
self._th.requestInterruption()
self._canceled = True

View File

@@ -13,6 +13,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
import helper import helper
import proginit as pi import proginit as pi
from backgroundworker import BackgroundWorker
from helper import WidgetData from helper import WidgetData
from ui.files_ui import Ui_win_files from ui.files_ui import Ui_win_files
@@ -22,6 +23,57 @@ class NodeType(IntEnum):
DIR = 1001 DIR = 1001
class UploadFiles(BackgroundWorker):
def __init__(self, file_list: list, parent):
super(UploadFiles, self).__init__(parent)
self.ec = 1
self.file_list = file_list
self.plc_program_included = False # Will be True, when opt_program was found in files
def run(self) -> None:
self.steps_todo.emit(len(self.file_list))
# Get config to find actual auto start program for warnings
opt_program = helper.cm.call_remote_function("get_config", default_value={})
opt_program = opt_program.get("plcprogram", "none.py")
progress_counter = 0
for file_name in self.file_list:
progress_counter += 1
# Remove base dir of file to set relative for PyLoad
send_name = file_name.replace(helper.cm.develop_watch_path, "")[1:]
self.status_message.emit(send_name)
# Check whether this is the auto start program
if send_name == opt_program:
self.plc_program_included = True
# Transfer file
try:
with open(file_name, "rb") as fh:
upload_status = helper.cm.call_remote_function(
"plcupload", Binary(gzip.compress(fh.read())), send_name,
default_value=False
)
except Exception as e:
pi.logger.error(e)
self.ec = -2
return
if not upload_status:
self.ec = -1
return
self.steps_done.emit(progress_counter)
if self.check_cancel():
return
self.ec = 0
class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files): class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -72,41 +124,13 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
) )
return return
# Get config to find actual auto start program for warnings uploader = UploadFiles(self.file_list_local(), self)
opt_program = helper.cm.call_remote_function("get_config", default_value={}) if uploader.exec_dialog() == QtWidgets.QDialog.Rejected:
opt_program = opt_program.get("plcprogram", "none.py") return
uploaded = True # Will be False, when opt_program was found in files
ec = 0
# todo: Do this in a thread with status bar to prevent freezing program on long upload times if uploader.ec == 0:
for file_name in self.file_list_local():
# todo: Check exception of local file
with open(file_name, "rb") as fh:
# Remove base dir of file to set relative for PyLoad
send_name = file_name.replace(helper.cm.develop_watch_path, "")[1:]
# Check whether this is the auto start program
if send_name == opt_program:
uploaded = False
# Transfer file
try:
upload_status = helper.cm.call_remote_function(
"plcupload", Binary(gzip.compress(fh.read())), send_name,
default_value=False
)
except Exception as e:
pi.logger.error(e)
ec = -2
break
if not upload_status:
ec = -1
break
if ec == 0:
# Tell user, we did not find the auto start program in files # Tell user, we did not find the auto start program in files
if uploaded: if not uploader.plc_program_included:
QtWidgets.QMessageBox.information( QtWidgets.QMessageBox.information(
self, self.tr("Information"), self.tr( self, self.tr("Information"), self.tr(
"A PLC program has been uploaded. Please check the " "A PLC program has been uploaded. Please check the "
@@ -115,7 +139,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
) )
) )
elif ec == -1: elif uploader.ec == -1:
QtWidgets.QMessageBox.critical( QtWidgets.QMessageBox.critical(
self, self.tr("Error"), self.tr( self, self.tr("Error"), self.tr(
"The Revolution Pi could not process some parts of the " "The Revolution Pi could not process some parts of the "
@@ -123,7 +147,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
) )
) )
elif ec == -2: elif uploader.ec == -2:
QtWidgets.QMessageBox.critical( QtWidgets.QMessageBox.critical(
self, self.tr("Error"), self, self.tr("Error"),
self.tr("Errors occurred during transmission") self.tr("Errors occurred during transmission")
@@ -489,16 +513,16 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
else: else:
file_name = os.path.join(helper.cm.develop_watch_path, file_name) file_name = os.path.join(helper.cm.develop_watch_path, file_name)
if override is None and os.path.exists(file_name): if override is None and os.path.exists(file_name):
rc = QtWidgets.QMessageBox.question( rc_diag = QtWidgets.QMessageBox.question(
self, self.tr("Override files..."), self.tr( self, self.tr("Override files..."), self.tr(
"One or more files does exist on your computer! Do you want to override the existing" "One or more files does exist on your computer! Do you want to override the existing"
"files?\n\nSelect 'Yes' to override, 'No' to download only missing files." "files?\n\nSelect 'Yes' to override, 'No' to download only missing files."
), ),
buttons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel buttons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel
) )
if rc == QtWidgets.QMessageBox.Cancel: if rc_diag == QtWidgets.QMessageBox.Cancel:
return return
override = rc == QtWidgets.QMessageBox.Yes override = rc_diag == QtWidgets.QMessageBox.Yes
if os.path.exists(file_name) and not override: if os.path.exists(file_name) and not override:
pi.logger.debug("Skip existing file '{0}'".format(file_name)) pi.logger.debug("Skip existing file '{0}'".format(file_name))