class FileSystemPane(QFrame): """ Contains two QListWidgets representing the micro:bit and the user's code directory. Users transfer files by dragging and dropping. Highlighted files can be selected for deletion. """ set_message = pyqtSignal(str) set_warning = pyqtSignal(str) list_files = pyqtSignal() def __init__(self, home): super().__init__() self.home = home self.font = Font().load() microbit_fs = MicrobitFileList(home) local_fs = LocalFileList(home) layout = QGridLayout() self.setLayout(layout) microbit_label = QLabel() microbit_label.setText(_('Files on your micro:bit:')) local_label = QLabel() local_label.setText(_('Files on your computer:')) self.microbit_label = microbit_label self.local_label = local_label self.microbit_fs = microbit_fs self.local_fs = local_fs self.set_font_size() layout.addWidget(microbit_label, 0, 0) layout.addWidget(local_label, 0, 1) layout.addWidget(microbit_fs, 1, 0) layout.addWidget(local_fs, 1, 1) self.microbit_fs.disable.connect(self.disable) self.microbit_fs.set_message.connect(self.show_message) self.local_fs.disable.connect(self.disable) self.local_fs.set_message.connect(self.show_message) def disable(self): """ Stops interaction with the list widgets. """ self.microbit_fs.setDisabled(True) self.local_fs.setDisabled(True) self.microbit_fs.setAcceptDrops(False) self.local_fs.setAcceptDrops(False) def enable(self): """ Allows interaction with the list widgets. """ self.microbit_fs.setDisabled(False) self.local_fs.setDisabled(False) self.microbit_fs.setAcceptDrops(True) self.local_fs.setAcceptDrops(True) def show_message(self, message): """ Emits the set_message signal. """ self.set_message.emit(message) def show_warning(self, message): """ Emits the set_warning signal. """ self.set_warning.emit(message) def on_ls(self, microbit_files): """ Displays a list of the files on the micro:bit. Since listing files is always the final event in any interaction between Mu and the micro:bit, this enables the controls again for further interactions to take place. """ self.microbit_fs.clear() self.local_fs.clear() for f in microbit_files: self.microbit_fs.addItem(f) local_files = [ f for f in os.listdir(self.home) if os.path.isfile(os.path.join(self.home, f)) ] local_files.sort() for f in local_files: self.local_fs.addItem(f) self.enable() def on_ls_fail(self): """ Fired when listing files fails. """ self.show_warning( _("There was a problem getting the list of files on " "the micro:bit. Please check Mu's logs for " "technical information. Alternatively, try " "unplugging/plugging-in your micro:bit and/or " "restarting Mu.")) self.disable() def on_put_fail(self, filename): """ Fired when the referenced file cannot be copied onto the micro:bit. """ self.show_warning( _("There was a problem copying the file '{}' onto " "the micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_delete_fail(self, filename): """ Fired when a deletion on the micro:bit for the given file failed. """ self.show_warning( _("There was a problem deleting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_get_fail(self, filename): """ Fired when getting the referenced file on the micro:bit failed. """ self.show_warning( _("There was a problem getting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def set_theme(self, theme): """ Sets the theme / look for the FileSystemPane. """ if theme == 'day': self.setStyleSheet(DAY_STYLE) elif theme == 'night': self.setStyleSheet(NIGHT_STYLE) else: self.setStyleSheet(CONTRAST_STYLE) def set_font_size(self, new_size=DEFAULT_FONT_SIZE): """ Sets the font size for all the textual elements in this pane. """ self.font.setPointSize(new_size) self.microbit_label.setFont(self.font) self.local_label.setFont(self.font) self.microbit_fs.setFont(self.font) self.local_fs.setFont(self.font) def zoomIn(self, delta=2): """ Zoom in (increase) the size of the font by delta amount difference in point size upto 34 points. """ old_size = self.font.pointSize() new_size = min(old_size + delta, 34) self.set_font_size(new_size) def zoomOut(self, delta=2): """ Zoom out (decrease) the size of the font by delta amount difference in point size down to 4 points. """ old_size = self.font.pointSize() new_size = max(old_size - delta, 4) self.set_font_size(new_size)
class FileSystemPane(QFrame): """ Contains two QListWidgets representing the micro:bit and the user's code directory. Users transfer files by dragging and dropping. Highlighted files can be selected for deletion. """ set_message = pyqtSignal(str) set_warning = pyqtSignal(str) list_files = pyqtSignal() open_file = pyqtSignal(str) def __init__(self, home): import ctypes from subprocess import check_output def find_device(): """ Returns a path on the filesystem that represents the plugged in BBC micro:bit that is to be flashed. If no micro:bit is found, it returns None. Works on Linux, OSX and Windows. Will raise a NotImplementedError exception if run on any other operating system. """ # Check what sort of operating system we're on. if os.name == 'posix': # 'posix' means we're on Linux or OSX (Mac). # Call the unix "mount" command to list the mounted volumes. mount_output = check_output('mount').splitlines() mounted_volumes = [x.split()[2] for x in mount_output] for volume in mounted_volumes: if volume.endswith(b'MINI') or volume.endswith( b'MICROBIT'): return volume.decode( 'utf-8') # Return a string not bytes. elif os.name == 'nt': # 'nt' means we're on Windows. def get_volume_name(disk_name): """ Each disk or external device connected to windows has an attribute called "volume name". This function returns the volume name for the given disk/device. Code from http://stackoverflow.com/a/12056414 """ vol_name_buf = ctypes.create_unicode_buffer(1024) ctypes.windll.kernel32.GetVolumeInformationW( ctypes.c_wchar_p(disk_name), vol_name_buf, ctypes.sizeof(vol_name_buf), None, None, None, None, 0) return vol_name_buf.value # # In certain circumstances, volumes are allocated to USB # storage devices which cause a Windows popup to raise if their # volume contains no media. Wrapping the check in SetErrorMode # with SEM_FAILCRITICALERRORS (1) prevents this popup. # old_mode = ctypes.windll.kernel32.SetErrorMode(1) try: for disk in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': path = '{}:\\'.format(disk) # # Don't bother looking if the drive isn't removable # if ctypes.windll.kernel32.GetDriveTypeW(path) != 2: continue if os.path.exists(path) and \ get_volume_name(path) == 'MINI' or get_volume_name(path) == 'MICROBIT': return get_volume_name(path) finally: ctypes.windll.kernel32.SetErrorMode(old_mode) else: # No support for unknown operating systems. #raise NotImplementedError('OS "{}" not supported.'.format(os.name)) return None super().__init__() self.home = home self.font = Font().load() microbit_fs = MicrobitFileList(home) local_fs = LocalFileList(home) self.device_displayName = "micro:bit" if find_device().lower().find("mini") > -1: microbit_fs = CalliopeMiniFileList(home) self.device_displayName = "Calliope mini" @local_fs.open_file.connect def on_open_file(file): # Bubble the signal up self.open_file.emit(file) layout = QGridLayout() self.setLayout(layout) microbit_label = QLabel() microbit_label.setText( _('Files on your {}:'.format(self.device_displayName))) local_label = QLabel() local_label.setText(_('Files on your computer:')) self.microbit_label = microbit_label self.local_label = local_label self.microbit_fs = microbit_fs self.local_fs = local_fs self.set_font_size() layout.addWidget(microbit_label, 0, 0) layout.addWidget(local_label, 0, 1) layout.addWidget(microbit_fs, 1, 0) layout.addWidget(local_fs, 1, 1) self.microbit_fs.disable.connect(self.disable) self.microbit_fs.set_message.connect(self.show_message) self.local_fs.disable.connect(self.disable) self.local_fs.set_message.connect(self.show_message) def disable(self): """ Stops interaction with the list widgets. """ self.microbit_fs.setDisabled(True) self.local_fs.setDisabled(True) self.microbit_fs.setAcceptDrops(False) self.local_fs.setAcceptDrops(False) def enable(self): """ Allows interaction with the list widgets. """ self.microbit_fs.setDisabled(False) self.local_fs.setDisabled(False) self.microbit_fs.setAcceptDrops(True) self.local_fs.setAcceptDrops(True) def show_message(self, message): """ Emits the set_message signal. """ self.set_message.emit(message) def show_warning(self, message): """ Emits the set_warning signal. """ self.set_warning.emit(message) def on_ls(self, microbit_files): """ Displays a list of the files on the micro:bit. Since listing files is always the final event in any interaction between Mu and the micro:bit, this enables the controls again for further interactions to take place. """ self.microbit_fs.clear() self.local_fs.clear() for f in microbit_files: self.microbit_fs.addItem(f) local_files = [ f for f in os.listdir(self.home) if os.path.isfile(os.path.join(self.home, f)) ] local_files.sort() for f in local_files: self.local_fs.addItem(f) self.enable() def on_ls_fail(self): """ Fired when listing files fails. """ self.show_warning( _("There was a problem getting the list of files on " "the micro:bit. Please check Mu's logs for " "technical information. Alternatively, try " "unplugging/plugging-in your micro:bit and/or " "restarting Mu.")) self.disable() def on_put_fail(self, filename): """ Fired when the referenced file cannot be copied onto the micro:bit. """ self.show_warning( _("There was a problem copying the file '{}' onto " "the micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_delete_fail(self, filename): """ Fired when a deletion on the micro:bit for the given file failed. """ self.show_warning( _("There was a problem deleting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_get_fail(self, filename): """ Fired when getting the referenced file on the micro:bit failed. """ self.show_warning( _("There was a problem getting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def set_theme(self, theme): pass def set_font_size(self, new_size=DEFAULT_FONT_SIZE): """ Sets the font size for all the textual elements in this pane. """ self.font.setPointSize(new_size) self.microbit_label.setFont(self.font) self.local_label.setFont(self.font) self.microbit_fs.setFont(self.font) self.local_fs.setFont(self.font) def zoomIn(self, delta=2): """ Zoom in (increase) the size of the font by delta amount difference in point size upto 34 points. """ old_size = self.font.pointSize() new_size = min(old_size + delta, 34) self.set_font_size(new_size) def zoomOut(self, delta=2): """ Zoom out (decrease) the size of the font by delta amount difference in point size down to 4 points. """ old_size = self.font.pointSize() new_size = max(old_size - delta, 4) self.set_font_size(new_size)
class FileSystemPane(QFrame): """ Contains two QListWidgets representing the micro:bit and the user's code directory. Users transfer files by dragging and dropping. Highlighted files can be selected for deletion. """ def __init__(self, home): super().__init__() self.home = home self.font = Font().load() microbit_fs = MicrobitFileList(home) local_fs = LocalFileList(home) layout = QGridLayout() self.setLayout(layout) microbit_label = QLabel() microbit_label.setText(_('Files on your micro:bit:')) local_label = QLabel() local_label.setText(_('Files on your computer:')) self.microbit_label = microbit_label self.local_label = local_label self.microbit_fs = microbit_fs self.local_fs = local_fs self.set_font_size() layout.addWidget(microbit_label, 0, 0) layout.addWidget(local_label, 0, 1) layout.addWidget(microbit_fs, 1, 0) layout.addWidget(local_fs, 1, 1) self.ls() def ls(self): """ Gets a list of the files on the micro:bit. Naive implementation for simplicity's sake. """ self.microbit_fs.clear() self.local_fs.clear() microbit_files = microfs.ls(microfs.get_serial()) for f in microbit_files: self.microbit_fs.addItem(f) local_files = [ f for f in os.listdir(self.home) if os.path.isfile(os.path.join(self.home, f)) ] local_files.sort() for f in local_files: self.local_fs.addItem(f) def set_theme(self, theme): """ Sets the theme / look for the FileSystemPane. """ if theme == 'day': self.setStyleSheet(DAY_STYLE) elif theme == 'night': self.setStyleSheet(NIGHT_STYLE) else: self.setStyleSheet(CONTRAST_STYLE) def set_font_size(self, new_size=DEFAULT_FONT_SIZE): """ Sets the font size for all the textual elements in this pane. """ self.font.setPointSize(new_size) self.microbit_label.setFont(self.font) self.local_label.setFont(self.font) self.microbit_fs.setFont(self.font) self.local_fs.setFont(self.font) def zoomIn(self, delta=2): """ Zoom in (increase) the size of the font by delta amount difference in point size upto 34 points. """ old_size = self.font.pointSize() new_size = min(old_size + delta, 34) self.set_font_size(new_size) def zoomOut(self, delta=2): """ Zoom out (decrease) the size of the font by delta amount difference in point size down to 4 points. """ old_size = self.font.pointSize() new_size = max(old_size - delta, 4) self.set_font_size(new_size)
class FileSystemPane(QFrame): """ Contains two QListWidgets representing the micro:bit and the user's code directory. Users transfer files by dragging and dropping. Highlighted files can be selected for deletion. """ set_message = pyqtSignal(str) set_warning = pyqtSignal(str) list_files = pyqtSignal() open_file = pyqtSignal(str) def __init__(self, home): super().__init__() self.home = home self.font = Font().load() microbit_fs = MicrobitFileList(home) local_fs = LocalFileList(home) @local_fs.open_file.connect def on_open_file(file): # Bubble the signal up self.open_file.emit(file) layout = QGridLayout() self.setLayout(layout) microbit_label = QLabel() microbit_label.setText(_('Files on your micro:bit:')) local_label = QLabel() local_label.setText(_('Files on your computer:')) self.microbit_label = microbit_label self.local_label = local_label self.microbit_fs = microbit_fs self.local_fs = local_fs self.set_font_size() layout.addWidget(microbit_label, 0, 0) layout.addWidget(local_label, 0, 1) layout.addWidget(microbit_fs, 1, 0) layout.addWidget(local_fs, 1, 1) self.microbit_fs.disable.connect(self.disable) self.microbit_fs.set_message.connect(self.show_message) self.local_fs.disable.connect(self.disable) self.local_fs.set_message.connect(self.show_message) def disable(self): """ Stops interaction with the list widgets. """ self.microbit_fs.setDisabled(True) self.local_fs.setDisabled(True) self.microbit_fs.setAcceptDrops(False) self.local_fs.setAcceptDrops(False) def enable(self): """ Allows interaction with the list widgets. """ self.microbit_fs.setDisabled(False) self.local_fs.setDisabled(False) self.microbit_fs.setAcceptDrops(True) self.local_fs.setAcceptDrops(True) def show_message(self, message): """ Emits the set_message signal. """ self.set_message.emit(message) def show_warning(self, message): """ Emits the set_warning signal. """ self.set_warning.emit(message) def on_ls(self, microbit_files): """ Displays a list of the files on the micro:bit. Since listing files is always the final event in any interaction between Mu and the micro:bit, this enables the controls again for further interactions to take place. """ self.microbit_fs.clear() self.local_fs.clear() for f in microbit_files: self.microbit_fs.addItem(f) local_files = [f for f in os.listdir(self.home) if os.path.isfile(os.path.join(self.home, f))] local_files.sort() for f in local_files: self.local_fs.addItem(f) self.enable() def on_ls_fail(self): """ Fired when listing files fails. """ self.show_warning(_("There was a problem getting the list of files on " "the micro:bit. Please check Mu's logs for " "technical information. Alternatively, try " "unplugging/plugging-in your micro:bit and/or " "restarting Mu.")) self.disable() def on_put_fail(self, filename): """ Fired when the referenced file cannot be copied onto the micro:bit. """ self.show_warning(_("There was a problem copying the file '{}' onto " "the micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_delete_fail(self, filename): """ Fired when a deletion on the micro:bit for the given file failed. """ self.show_warning(_("There was a problem deleting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def on_get_fail(self, filename): """ Fired when getting the referenced file on the micro:bit failed. """ self.show_warning(_("There was a problem getting '{}' from the " "micro:bit. Please check Mu's logs for " "more information.").format(filename)) def set_theme(self, theme): pass def set_font_size(self, new_size=DEFAULT_FONT_SIZE): """ Sets the font size for all the textual elements in this pane. """ self.font.setPointSize(new_size) self.microbit_label.setFont(self.font) self.local_label.setFont(self.font) self.microbit_fs.setFont(self.font) self.local_fs.setFont(self.font) def zoomIn(self, delta=2): """ Zoom in (increase) the size of the font by delta amount difference in point size upto 34 points. """ old_size = self.font.pointSize() new_size = min(old_size + delta, 34) self.set_font_size(new_size) def zoomOut(self, delta=2): """ Zoom out (decrease) the size of the font by delta amount difference in point size down to 4 points. """ old_size = self.font.pointSize() new_size = max(old_size - delta, 4) self.set_font_size(new_size)