diff --git a/mu/contrib/microfs.py b/mu/contrib/microfs.py index df868a57d..c8c56c950 100644 --- a/mu/contrib/microfs.py +++ b/mu/contrib/microfs.py @@ -119,7 +119,7 @@ def get_serial(): return Serial(port, SERIAL_BAUD_RATE, timeout=1, parity="N") -def execute(commands, serial=None): +def execute(commands, serial=None, show_progress=False, callback=None): """ Sends the command to the connected micro:bit via serial and returns the result. If no serial connection is provided, attempts to autodetect the @@ -139,17 +139,22 @@ def execute(commands, serial=None): raw_on(serial) time.sleep(0.1) # Write the actual command and send CTRL-D to evaluate. - for command in commands: + total_size = len(commands) + for cnt, command in enumerate(commands): command_bytes = command.encode("utf-8") + # Up to 32 bytes can be stored separately in both transmit and receive modes of the UART for i in range(0, len(command_bytes), 32): serial.write(command_bytes[i : min(i + 32, len(command_bytes))]) time.sleep(0.01) - serial.write(b"\x04") + serial.write(b"\x04") # Soft Reset with CTRL-D response = serial.read_until(b"\x04>") # Read until prompt. - out, err = response[2:-2].split(b"\x04", 1) # Split stdout, stderr - result += out - if err: - return b"", err + if len(response) > 3: # Check if the split is possible + out, err = response[2:-2].split(b"\x04", 1) # Split stdout, stderr + result += out + if err: + return b"", err + if show_progress: + callback.emit(round(cnt / total_size * 100)) time.sleep(0.1) raw_off(serial) if close_serial: @@ -204,7 +209,7 @@ def rm(filename, serial=None): return True -def put(filename, target=None, serial=None): +def put(self, filename, target=None, serial=None): """ Puts a referenced file on the LOCAL file system onto the file system on the BBC micro:bit. @@ -230,13 +235,13 @@ def put(filename, target=None, serial=None): commands.append("f(" + repr(line) + ")") content = content[64:] commands.append("fd.close()") - out, err = execute(commands, serial) + out, err = execute(commands, serial, True, self.on_put_update_file) if err: raise IOError(clean_error(err)) return True -def get(filename, target=None, serial=None): +def get(self, filename, target=None, serial=None): """ Gets a referenced file on the device's file system and copies it to the target (or current working directory if unspecified). @@ -270,12 +275,9 @@ def get(filename, target=None, serial=None): "while result:\n result = r(32)\n if result:\n u.write(result)\n", "f.close()", ] - out, err = execute(commands, serial) + out, err = execute(commands, serial, True, self.on_put_update_file) if err: raise IOError(clean_error(err)) - # Recombine the bytes while removing "b'" from start and "'" from end. - with open(target, "wb") as f: - f.write(out) return True diff --git a/mu/interface/main.py b/mu/interface/main.py index 0124eea14..d6443cf6d 100644 --- a/mu/interface/main.py +++ b/mu/interface/main.py @@ -39,6 +39,7 @@ QPushButton, QHBoxLayout, QProgressDialog, + QProgressBar, ) from PyQt5.QtGui import QKeySequence, QStandardItemModel, QCursor from mu import __version__ @@ -627,8 +628,10 @@ def on_open_file(file): self.fs_pane.microbit_fs.list_files.connect(file_manager.ls) self.fs_pane.local_fs.get.connect(file_manager.get) self.fs_pane.local_fs.put.connect(file_manager.put) + self.fs_pane.local_fs.pbar_update.connect(self.fs_pane.microbit_fs.on_put_update) self.fs_pane.local_fs.list_files.connect(file_manager.ls) file_manager.on_put_file.connect(self.fs_pane.microbit_fs.on_put) + file_manager.on_put_update_file.connect(self.fs_pane.microbit_fs.on_put_update) file_manager.on_delete_file.connect(self.fs_pane.microbit_fs.on_delete) file_manager.on_get_file.connect(self.fs_pane.local_fs.on_get) file_manager.on_list_fail.connect(self.fs_pane.on_ls_fail) @@ -1463,6 +1466,10 @@ def __init__(self, parent=None, mode="python"): self.mode = mode self.msg_duration = 5 + # Progress bar + self.progress_bar = QProgressBar() + self.addPermanentWidget(self.progress_bar) + # Mode selector. self.mode_label = QLabel() self.mode_label.setToolTip(_("Mu's current mode of behaviour.")) @@ -1527,3 +1534,9 @@ def device_connected(self, device): msg = _("Detected new {} device.").format(device.long_mode_name) self.set_message(msg, self.msg_duration * 1000) + + def set_pbar_value(self, amount): + self.progress_bar.setVisible(True) + self.progress_bar.setValue(amount) + if amount >= 100 or amount < 0: + self.progress_bar.setVisible(False) diff --git a/mu/interface/panes.py b/mu/interface/panes.py index 11890eb24..8bd5cb04a 100644 --- a/mu/interface/panes.py +++ b/mu/interface/panes.py @@ -682,6 +682,7 @@ class MuFileList(QListWidget): disable = pyqtSignal() list_files = pyqtSignal() set_message = pyqtSignal(str) + pbar_update = pyqtSignal(int) def show_confirm_overwrite_dialog(self): """ @@ -737,6 +738,10 @@ def on_put(self, microbit_file): msg = _("'{}' successfully copied to device.").format(microbit_file) self.set_message.emit(msg) self.list_files.emit() + self.pbar_update.emit(-1) # To remove the pbar UI + + def on_put_update(self, amount): + self.pbar_update.emit(amount) def contextMenuEvent(self, event): menu_current_item = self.currentItem() @@ -807,6 +812,7 @@ def on_get(self, microbit_file): ).format(microbit_file) self.set_message.emit(msg) self.list_files.emit() + self.pbar_update.emit(-1) # To remove the pbar UI def contextMenuEvent(self, event): menu_current_item = self.currentItem() @@ -858,6 +864,7 @@ class FileSystemPane(QFrame): set_warning = pyqtSignal(str) list_files = pyqtSignal() open_file = pyqtSignal(str) + set_pbar_update = pyqtSignal(int) def __init__(self, home): super().__init__() @@ -888,6 +895,7 @@ def on_open_file(file): layout.addWidget(local_fs, 1, 1) self.microbit_fs.disable.connect(self.disable) self.microbit_fs.set_message.connect(self.show_message) + self.microbit_fs.pbar_update.connect(self.show_progressbar_update) self.local_fs.disable.connect(self.disable) self.local_fs.set_message.connect(self.show_message) @@ -921,6 +929,12 @@ def show_warning(self, message): """ self.set_warning.emit(message) + def show_progressbar_update(self, amount): + """ + Emits the set_pbar_update signal. + """ + self.set_pbar_update.emit(amount) + def on_ls(self, microbit_files): """ Displays a list of the files on the micro:bit. diff --git a/mu/logic.py b/mu/logic.py index ea0bcb4c3..ba752a5a6 100644 --- a/mu/logic.py +++ b/mu/logic.py @@ -1837,6 +1837,12 @@ def show_status_message(self, message, duration=5): """ self._view.status_bar.set_message(message, duration * 1000) + def show_progressbar_update(self, amount): + """ + Displays the referenced amount on the progress bar. + """ + self._view.status_bar.set_pbar_value(amount) + def debug_toggle_breakpoint(self, margin, line, modifiers): """ How to handle the toggling of a breakpoint. diff --git a/mu/modes/base.py b/mu/modes/base.py index e3798081a..b244e9ff1 100644 --- a/mu/modes/base.py +++ b/mu/modes/base.py @@ -658,6 +658,8 @@ class FileManager(QObject): on_get_file = pyqtSignal(str) # Emitted when the file with referenced filename is put onto the device. on_put_file = pyqtSignal(str) + # Emitted while the file with referenced filename is put onto the device. + on_put_update_file = pyqtSignal(int) # Emitted when the file with referenced filename is deleted from the # device. on_delete_file = pyqtSignal(str) @@ -714,11 +716,12 @@ def get(self, device_filename, local_filename): failure signal. """ try: - microfs.get(device_filename, local_filename, serial=self.serial) + microfs.get(self, device_filename, local_filename, serial=self.serial) self.on_get_file.emit(device_filename) except Exception as ex: logger.error(ex) self.on_get_fail.emit(device_filename) + self.on_put_update_file.emit(-1) # To remove the pbar UI def put(self, local_filename, target=None): """ @@ -727,11 +730,12 @@ def put(self, local_filename, target=None): a failure signal. """ try: - microfs.put(local_filename, target=target, serial=self.serial) + microfs.put(self, local_filename, target=target, serial=self.serial) self.on_put_file.emit(os.path.basename(local_filename)) except Exception as ex: logger.error(ex) self.on_put_fail.emit(local_filename) + self.on_put_update_file.emit(-1) # To remove the pbar UI def delete(self, device_filename): """ diff --git a/mu/modes/esp.py b/mu/modes/esp.py index 50038f2dd..d09ad2d04 100644 --- a/mu/modes/esp.py +++ b/mu/modes/esp.py @@ -23,7 +23,6 @@ from PyQt5.QtCore import QThread import os from mu.resources import load_icon -import time logger = logging.getLogger(__name__) @@ -285,6 +284,7 @@ def add_fs(self): ) self.fs.set_message.connect(self.editor.show_status_message) self.fs.set_warning.connect(self.view.show_message) + self.fs.set_pbar_update.connect(self.editor.show_progressbar_update) self.file_manager_thread.start() def remove_fs(self):