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 __init__(self, home): super().__init__() self.home = home self.font = Font().load() microbit_fs = ArdupyDeviceFileList(home) local_fs = LocalFileTree(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 device:")) 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.enable.connect(self.enable) self.local_fs.set_message.connect(self.show_message)
def configure(self): """ Set up the editor component. """ # Font information font = Font().load() self.setFont(font) # Generic editor settings self.setUtf8(True) self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setIndentationGuides(True) self.setBackspaceUnindents(True) self.setTabWidth(4) self.setEdgeColumn(79) self.setMarginLineNumbers(0, True) self.setMarginWidth(0, 50) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.set_theme() # Markers and indicators self.setMarginSensitivity(0, True) self.markerDefine(self.Circle, self.BREAKPOINT_MARKER) self.setMarginSensitivity(1, True) self.setIndicatorDrawUnder(True) for type_ in self.check_indicators: self.indicatorDefine(self.SquiggleIndicator, self.check_indicators[type_]['id']) for type_ in self.search_indicators: self.indicatorDefine(self.StraightBoxIndicator, self.search_indicators[type_]['id']) self.indicatorDefine(self.FullBoxIndicator, self.DEBUG_INDICATOR) self.setAnnotationDisplay(self.AnnotationBoxed) self.selectionChanged.connect(self.selection_change_listener)
def __init__(self, port, theme='day', parent=None): super().__init__(parent) self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.setObjectName('replpane') # open the serial port self.serial = QSerialPort(self) self.serial.setPortName(port) if self.serial.open(QIODevice.ReadWrite): self.serial.dataTerminalReady = True if not self.serial.isDataTerminalReady(): # Using pyserial as a 'hack' to open the port and set DTR # as QtSerial does not seem to work on some Windows :( # See issues #281 and #302 for details. self.serial.close() pyser = serial.Serial(port) # open serial port w/pyserial pyser.dtr = True pyser.close() self.serial.open(QIODevice.ReadWrite) self.serial.setBaudRate(115200) self.serial.readyRead.connect(self.on_serial_read) # clear the text self.clear() # Send a Control-C self.serial.write(b'\x03') else: raise IOError("Cannot connect to device on port {}".format(port)) self.set_theme(theme)
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 __init__(self, serial, theme='day', parent=None): super().__init__(parent) self.serial = serial self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.setObjectName('replpane') self.set_theme(theme)
def __init__(self, parent=None): super().__init__(parent) self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.setObjectName('PythonRunner') self.process = None # Will eventually reference the running process. self.input_history = [] # history of inputs entered in this session. self.start_of_current_line = 0 # start position of the input line. self.history_position = 0 # current position when navigation history.
def __init__(self, parent=None): super().__init__(parent) self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.running = False # Flag to show the child process is running. self.setObjectName('PythonRunner') self.process = None # Will eventually reference the running process. self.input_history = [] # history of inputs entered in this session. self.start_of_current_line = 0 # start position of the input line. self.history_position = 0 # current position when navigation history. self.stdout_buffer = b'' # contains non-decoded bytes from stdout. self.reading_stdout = False # flag showing if already reading stdout.
def configure(self): """ Set up the editor component. """ # Font information font = Font().load() self.setFont(font) # Generic editor settings self.setUtf8(True) self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setIndentationGuides(True) self.setBackspaceUnindents(True) self.setTabWidth(4) self.setEdgeColumn(79) self.setMarginLineNumbers(0, True) self.setMarginWidth(0, 50) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.set_theme() # Markers and indicators self.setMarginSensitivity(0, True) self.markerDefine(self.Circle, self.BREAKPOINT_MARKER) self.setMarginSensitivity(1, True) # Additional dummy margin to prevent accidental breakpoint toggles when # trying to position the edit cursor to the left of the first column, # using the mouse and not being 100% accurate. This margin needs to be # set with "sensitivity on": otherwise clicking it would select the # whole text line, per QsciScintilla's behaviour. It is up to the # click handler to ignore clicks on this margin: self.connect_margin. self.setMarginWidth(4, 8) self.setMarginSensitivity(4, True) # Indicators self.setIndicatorDrawUnder(True) for type_ in self.check_indicators: self.indicatorDefine( self.SquiggleIndicator, self.check_indicators[type_]["id"] ) for type_ in self.search_indicators: self.indicatorDefine( self.StraightBoxIndicator, self.search_indicators[type_]["id"] ) self.indicatorDefine(self.FullBoxIndicator, self.DEBUG_INDICATOR) self.setAnnotationDisplay(self.AnnotationBoxed) self.selectionChanged.connect(self.selection_change_listener) self.set_zoom()
def __init__(self, connection, theme="day", parent=None): super().__init__(parent) self.connection = connection self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) # The following variable maintains the position where we know # the device cursor is placed. It is initialized to the beginning # of the QTextEdit (i.e. equal to the Qt cursor position) self.device_cursor_position = self.textCursor().position() self.setObjectName("replpane") self.set_theme(theme) self.unprocessed_input = b"" # used by process_bytes self.decoder = codecs.getincrementaldecoder("utf8")("replace") self.vt100_regex = re.compile( r"\x1B\[(?P<count>[\d]*)(;?[\d]*)*(?P<action>[A-Za-z])" )
def __init__(self, port, theme='day', parent=None): super().__init__(parent) self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.setObjectName('replpane') # open the serial port self.serial = QSerialPort(self) self.serial.setPortName(port) if self.serial.open(QIODevice.ReadWrite): self.serial.setBaudRate(115200) self.serial.readyRead.connect(self.on_serial_read) # clear the text self.clear() # Send a Control-C self.serial.write(b'\x03') else: raise IOError("Cannot connect to device on port {}".format(port)) self.set_theme(theme)
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 set_zoom(self, size): """ Set the current zoom level given the "t-shirt" size. """ self.set_font_size(PANE_ZOOM_SIZES[size])
class SeeedFileSystemPane(QFrame): 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 = ArdupyDeviceFileList(home) local_fs = LocalFileTree(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 device:")) 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.enable.connect(self.enable) 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 seeed board. Since listing files is always the final event in any interaction between Mu and the seeed board, this enables the controls again for further interactions to take place. """ print("SeeedFileSystemPane on_ls") print(microbit_files) self.microbit_fs.clear() for f in microbit_files: self.microbit_fs.addItem(f) if self.local_fs.need_update_tree: self.local_fs.clear() self.local_fs.ls() else: self.local_fs.need_update_tree = True self.enable() def on_ls_fail(self): """ Fired when listing files fails. """ self.show_warning( _("There was a problem gettingthe list of files on " "the device. Please check Mu's logs for " "technical information. Alternatively, try " "unplugging/plugging-in your device and/or " "restarting Mu.")) self.disable() def on_put_fail(self, filename): """ Fired when the referenced file cannot be copied onto the device. """ self.show_warning( _("There was a problem copying the file '{}' onto " "the device. Please check Mu's logs for " "more information.").format(filename)) def on_delete_fail(self, filename): """ Fired when a deletion on the device for the given file failed. """ self.show_warning( _("There was a problem deleting '{}' from the " "device. Please check Mu's logs for " "more information.").format(filename)) def on_get_fail(self, filename): """ Fired when getting the referenced file on the device failed. """ self.show_warning( _("There was a problem getting '{}' from the " "device. 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 set_zoom(self, size): """ Set the current zoom level given the "t-shirt" size. """ self.set_font_size(PANE_ZOOM_SIZES[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. """ 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)
def __init__(self, parent=None): super().__init__(parent) self.setFont(Font().load()) self.input_buffer = []
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)
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)