def __init__(self, parent): super(FlashDialog, self).__init__(parent, Qt.WindowCloseButtonHint) self.setupUi(self) self.setModal(True) self._connection_scanner = ConnectionScanner() self._port = None self._flash_output = None self._flash_output_mutex = Lock() self._flashing = False if Settings().python_flash_executable: self.pythonPathEdit.setText(Settings().python_flash_executable) self.pickPythonButton.clicked.connect(self._pick_python) self.pickFirmwareButton.clicked.connect(self._pick_firmware) self.refreshButton.clicked.connect(self._refresh_ports) self.wiringButton.clicked.connect(self._show_wiring) self.eraseButton.clicked.connect(lambda: self._start(False, True)) self.flashButton.clicked.connect(lambda: self._start(True, False)) self._flash_output_signal.connect(self._update_output) self._flash_finished_signal.connect(self._flash_finished) self._refresh_ports()
def remove_preset(self): idx = self.presetsListView.currentIndex() assert isinstance(idx, QModelIndex) if idx.row() < 0: return Settings().wifi_presets.remove(Settings().wifi_presets[idx.row()]) self.update_preset_list()
def closeEvent(self, event): Settings.root_dir = self._root_dir Settings.save() if self._connection is not None and self._connection.is_connected(): self.end_connection() if self._terminal_dialog: assert isinstance(self._terminal_dialog, QDialog) self._terminal_dialog.close() event.accept()
def __init__(self, parent): super(SettingsDialog, self).__init__(parent, Qt.WindowCloseButtonHint) self.setupUi(self) self.setModal(True) # Workaround because UI compiler doesn't recognize QKeySequenceEdit # Create new items new_line_key_edit = SettingsDialog.one_key_sequence_edit(self.terminalGroupBox, "newLineKeyEdit") send_key_edit = SettingsDialog.one_key_sequence_edit(self.terminalGroupBox, "sendKeyEdit") # Replace old items in layout self.terminalFormLayout.replaceWidget(self.newLineKeyEdit, new_line_key_edit) self.terminalFormLayout.replaceWidget(self.sendKeyEdit, send_key_edit) # Set parent to None effectively removing old items self.newLineKeyEdit.setParent(None) self.sendKeyEdit.setParent(None) # Replace references self.newLineKeyEdit = new_line_key_edit self.sendKeyEdit = send_key_edit if Settings().external_editor_path: self.externalPathLineEdit.setText(Settings().external_editor_path) if Settings().external_editor_args: self.externalCommandLineEdit.setText(Settings().external_editor_args) self.externalPathBrowseButton.clicked.connect(self.browse_external_editor) self.newLineKeyEdit.setKeySequence(Settings().new_line_key) self.sendKeyEdit.setKeySequence(Settings().send_key) self.tabSpacesSpinBox.setValue(Settings().terminal_tab_spaces) if Settings().mpy_cross_path: self.mpyCrossPathLineEdit.setText(Settings().mpy_cross_path) self.mpyPathBrowseButton.clicked.connect(self.browse_mpy_cross)
def open_external_editor(self, file_path): ext_path = Settings().external_editor_path ext_args = [] if Settings().external_editor_args: def wildcard_replace(s): s = s.replace("%f", file_path) return s ext_args = [wildcard_replace(x.strip()) for x in Settings().external_editor_args.split(";")] subprocess.Popen([ext_path] + ext_args)
def _transfer_file_path(transfer_file_name): # External transfer scripts folder should be used (use case: files need to be edited) if Settings().external_transfer_scripts_folder: path = "".join([Settings().external_transfer_scripts_folder, "/", transfer_file_name]) # Check if file exists. If not, ignore external folder path. if os.path.isfile(path): return path else: raise FileNotFoundError return PyInstallerHelper.resource_path("mcu/" + transfer_file_name)
def closeEvent(self, event): Settings().root_dir = self._root_dir Settings().auto_transfer = self.autoTransferCheckBox.isChecked() Settings().update_geometry("main", self.saveGeometry()) Settings().save() if self._connection is not None and self._connection.is_connected(): self.end_connection() if self._terminal_dialog: self._terminal_dialog.close() if self._code_editor: self._code_editor.close() event.accept()
def _pick_firmware(self): firmware_dir = None if Settings().last_firmware_directory: firmware_dir = Settings().last_firmware_directory p = QFileDialog.getOpenFileName(parent=self, caption="Select python executable", directory=firmware_dir, filter="*.bin") path = p[0] if path: self.firmwarePathEdit.setText(path) Settings().last_firmware_directory = "/".join( path.split("/")[0:-1])
def save_settings(self): Settings().external_editor_path = self.externalPathLineEdit.text() Settings().external_editor_args = self.externalCommandLineEdit.text() Settings().new_line_key = self.newLineKeyEdit.keySequence() Settings().send_key = self.sendKeyEdit.keySequence() Settings().terminal_tab_spaces = self.tabSpacesSpinBox.value() Settings().mpy_cross_path = self.mpyCrossPathLineEdit.text() Settings().preferred_port = self.preferredPortLineEdit.text() Settings( ).external_transfer_scripts_folder = self.transferFilesPathLineEdit.text( ) Settings().save()
def _pick_python(self): p = QFileDialog.getOpenFileName(parent=self, caption="Select python executable") path = p[0] if path: self.pythonPathEdit.setText(path) Settings().python_flash_executable = path
def compile_files(self): local_file_paths = self.get_local_file_selection() compiled_file_paths = [] for local_path in local_file_paths: split = os.path.splitext(local_path) if split[1] == ".mpy": title = "COMPILE WARNING!! " + os.path.basename(local_path) QMessageBox.warning(self, title, "Can't compile .mpy files, already bytecode") continue mpy_path = split[0]+".mpy" try: os.remove(mpy_path) # Force view to update itself so that it sees file removed self.localFilesTreeView.repaint() QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) except OSError: pass try: with subprocess.Popen([Settings().mpy_cross_path, os.path.basename(local_path)], cwd=os.path.dirname(local_path), stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) as proc: proc.wait() # Wait for process to finish out = proc.stderr.read() if out: QMessageBox.warning(self, "Compilation error", out.decode("utf-8")) except OSError: QMessageBox.warning(self, "Compilation error", "Failed to run mpy-cross") compiled_file_paths += [mpy_path] # Force view to update so that it sees compiled files added self.localFilesTreeView.repaint() QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) # Force view to update last time so that it resorts the content # Without this, the new file will be placed at the bottom of the view no matter what header = self.localFilesTreeView.header() column = header.sortIndicatorSection() order = header.sortIndicatorOrder() # This is necessary so that view actually sorts anything again self.localFilesTreeView.sortByColumn(-1, Qt.AscendingOrder) QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.localFilesTreeView.sortByColumn(column, order) selection_model = self.localFilesTreeView.selectionModel() if compiled_file_paths: assert isinstance(selection_model, QItemSelectionModel) selection_model.clearSelection() for mpy_path in compiled_file_paths: idx = self.localFilesTreeView.model().index(mpy_path) selection_model.select(idx, QItemSelectionModel.Select | QItemSelectionModel.Rows) if self.autoTransferCheckBox.isChecked() and self._connection and self._connection.is_connected(): self.transfer_to_mcu()
def update_preset_list(self): self.model.removeRows(0, self.model.rowCount()) for preset in Settings().wifi_presets: idx = self.model.rowCount() self.model.insertRow(idx) name, ip, port, _ = preset text = "{}\nIP: {} Port: {}".format(name, ip, port) self.model.setData(self.model.index(idx), text)
def select_preset(self): idx = self.presetsListView.currentIndex() assert isinstance(idx, QModelIndex) if idx.row() < 0: return _, self.selected_ip, self.selected_port, self.selected_password = Settings( ).wifi_presets[idx.row()] self.accept()
def save_settings(self): Settings().external_editor_path = self.externalPathLineEdit.text() Settings().external_editor_args = self.externalCommandLineEdit.text() Settings().new_line_key = self.newLineKeyEdit.keySequence() Settings().send_key = self.sendKeyEdit.keySequence() Settings().terminal_tab_spaces = self.tabSpacesSpinBox.value() Settings().mpy_cross_path = self.mpyCrossPathLineEdit.text() Settings().save()
def _flash_job(self, python_path, firmware_file, erase_flash): try: params = [python_path, "flash.py", self._port] if firmware_file: params.append("--fw={}".format(firmware_file)) if erase_flash: params.append("--erase") if Settings().debug_mode: params.append("--debug") with subprocess.Popen(params, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1) as sub: buf = bytearray() delete = 0 Logger.log("Pipe receiving:\r\n") while True: x = sub.stdout.read(1) Logger.log(x) # Flushing content only in large blocks helps with flickering # Flush output if: # - didn't receive any character (timeout?) # - received first backspace (content is going to be deleted) # - received whitespace or dot (used to signal progress) if not x \ or (x[0] == 8 and delete == 0) \ or (x[0] in b"\r\n\t ."): with self._flash_output_mutex: if delete > 0: self._flash_output = self._flash_output[: -delete] self._flash_output.extend(buf) self._flash_output_signal.emit() buf = bytearray() delete = 0 if not x: break if x[0] == 8: delete += 1 else: buf.append(x[0]) Logger.log("\r\nPipe end.\r\n") sub.stdout.close() code = sub.wait() self._flash_finished_signal.emit(code) except (FileNotFoundError, OSError): self._flash_finished_signal.emit(-1) return
def log(x): if not Settings().debug_mode: return if not Logger._log_file: Logger._log_file = open("log.txt", "w+b", 0) if isinstance(x, str): x = x.encode('utf-8') Logger._log_file.write(x)
def _read_file_job(self, file_name, transfer): self._auto_reader_lock.acquire() self._auto_read_enabled = False if Settings().use_transfer_scripts: self.run_file("__download.py", "file_name=\"{}\"".format(file_name)) else: self.send_download_file(file_name) self.read_junk() self.recv_file(transfer) self._auto_read_enabled = True self._auto_reader_lock.release()
def _write_file_job(self, file_name, text, transfer): if isinstance(text, str): text = text.encode('utf-8') self._auto_reader_lock.acquire() self._auto_read_enabled = False if Settings().use_transfer_scripts: self.run_file("__upload.py", "file_name=\"{}\"".format(file_name)) else: self.send_upload_file(file_name) self.read_junk() self.send_file(text, transfer) self._auto_read_enabled = True self._auto_reader_lock.release()
def start_connection(self): self.set_status("Connecting...") connection = self._connection_scanner.port_list[self.connectionComboBox.currentIndex()] if connection == "wifi": ip_address = self.ipLineEdit.text() port = self.portSpinBox.value() if not IpHelper.is_valid_ipv4(ip_address): QMessageBox().warning(self, "Invalid IP", "The IP address has invalid format", QMessageBox.Ok) return try: self._connection = WifiConnection(ip_address, port, self._terminal, self.ask_for_password) except PasswordException: self.set_status("Password") return except NewPasswordException: QMessageBox().information(self, "Password set", "WebREPL password was not previously configured, so it was set to " "\"passw\" (without quotes). " "You can change it in port_config.py (will require reboot to take effect). " "Caution: Passwords longer than 9 characters will be truncated.\n\n" "Continue by connecting again.", QMessageBox.Ok) return else: baud_rate = BaudOptions.speeds[self.baudComboBox.currentIndex()] self._connection = SerialConnection(connection, baud_rate, self._terminal, self.serialResetCheckBox.isChecked()) if self._connection.is_connected(): if not self.serial_mcu_connection_valid(): self._connection.disconnect() self._connection = None else: # serial connection didn't work, so likely the unplugged the serial device and COM value is stale self.refresh_ports() if self._connection is not None and self._connection.is_connected(): self.connected() if isinstance(self._connection, SerialConnection): if Settings().use_transfer_scripts and not self._connection.check_transfer_scripts_version(): QMessageBox.warning(self, "Transfer scripts problem", "Transfer scripts for UART are either" " missing or have wrong version.\nPlease use 'File->Init transfer files' to" " fix this issue.") else: self._connection = None self.set_status("Error") self.refresh_ports()
def _read_file_job(self, file_name, transfer): self._auto_reader_lock.acquire() self._auto_read_enabled = False transfer_ready = True if Settings().use_transfer_scripts: self.run_file("__download.py", "file_name=\"{}\"".format(file_name)) else: try: self.send_download_file(file_name) except FileNotFoundError: transfer_ready = False transfer.mark_error() if transfer_ready: self.read_junk() self.recv_file(transfer) self._auto_read_enabled = True self._auto_reader_lock.release()
def refresh_ports(self): self._connection_scanner.scan_connections(with_wifi=True) # Populate port combo box and select default self.connectionComboBox.clear() if self._connection_scanner.port_list: for port in self._connection_scanner.port_list: self.connectionComboBox.addItem(port) prefPort = str(Settings().preferred_port) prefPort = prefPort.upper() prefPort = prefPort.join(prefPort.split()) if self.connectionComboBox.findText(prefPort) >= 0: self.connectionComboBox.setCurrentIndex(self.connectionComboBox.findText(prefPort)) else: self.connectionComboBox.setCurrentIndex(0) self.connectButton.setEnabled(True) else: self.connectButton.setEnabled(False)
def open_local_file(self, idx): assert isinstance(idx, QModelIndex) model = self.localFilesTreeView.model() assert isinstance(model, QFileSystemModel) if model.isDir(idx): return local_path = model.filePath(idx) remote_path = local_path.rsplit("/", 1)[1] if local_path.endswith(".py"): if Settings().external_editor_path: self.open_external_editor(local_path) else: with open(local_path) as f: text = "".join(f.readlines()) self.open_code_editor() self._code_editor.set_code(local_path, remote_path, text) else: QMessageBox.information(self, "Unknown file", "Files without .py ending won't open" " in editor, but can still be transferred.")
def __init__(self, parent, connection): super(CodeEditDialog, self).__init__(None, Qt.WindowCloseButtonHint) self.setupUi(self) geometry = Settings().retrieve_geometry("editor") if geometry: self.restoreGeometry(geometry) self._connection = connection self.saveLocalButton.clicked.connect(self._save_local) self.saveMcuButton.clicked.connect(self._save_to_mcu) #self.runButton.clicked.connect(self._run_file) self.runButton.hide() fixed_font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.codeEdit.setFont(fixed_font) if connection and connection.is_connected(): self.connected(connection) else: self.disconnected()
def run_game(): all_setting = Settings() pygame.init() screen = pygame.display.set_mode((all_setting.width, all_setting.height)) ship = Ship(screen, all_setting) pygame.display.set_caption('my first bullshit') bulls = Group() monsters = Group() while (True): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: gf.check_event_keydown(ship, bulls, all_setting, screen, event) elif event.type == pygame.KEYUP: gf.check_event_keyup(ship, bulls, all_setting, screen, event) ship.move() bulls.update() gf.update_screen(screen, ship, all_setting, bulls)
def add_preset(self): name = self.nameLineEdit.text() ip = self.ipLineEdit.text() port = self.portSpinBox.value() password = self.passwordLineEdit.text() # Make sure password is non if empty if not password: password = None if not name: QMessageBox().warning(self, "Missing name", "Fill the name of preset", QMessageBox.Ok) return if not IpHelper.is_valid_ipv4(ip): QMessageBox().warning(self, "Invalid IP", "The IP address has invalid format", QMessageBox.Ok) return Settings().wifi_presets.append((name, ip, port, password)) self.update_preset_list()
def send_bytes(self, binary): self._serial.write(binary) time.sleep(Settings().send_sleep)
def send_character(self, char): assert isinstance(char, str) self._serial.write(char.encode('utf-8')) time.sleep(Settings().send_sleep)
def send_line(self, line_text, ending="\r\n"): assert isinstance(line_text, str) assert isinstance(ending, str) self._serial.write((line_text + ending).encode('utf-8')) time.sleep(Settings().send_sleep)
def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.setAttribute(Qt.WA_QuitOnClose) geometry = Settings().retrieve_geometry("main") if geometry: self.restoreGeometry(geometry) self._connection_scanner = ConnectionScanner() self._connection = None self._root_dir = Settings().root_dir self._mcu_files_model = None self._terminal = Terminal() self._terminal_dialog = None self._code_editor = None self._flash_dialog = None self._settings_dialog = None self._preset_password = None self.actionNavigate.triggered.connect(self.navigate_directory) self.actionTerminal.triggered.connect(self.open_terminal) self.actionCode_Editor.triggered.connect(self.open_code_editor) self.actionUpload.triggered.connect(self.upload_transfer_scripts) self.actionFlash.triggered.connect(self.open_flash_dialog) self.actionSettings.triggered.connect(self.open_settings_dialog) self.connectionComboBox.currentIndexChanged.connect(self.connection_changed) self.refreshButton.clicked.connect(self.refresh_ports) # Populate baud speed combo box and select default self.baudComboBox.clear() for speed in BaudOptions.speeds: self.baudComboBox.addItem(str(speed)) self.baudComboBox.setCurrentIndex(BaudOptions.speeds.index(115200)) self.presetButton.clicked.connect(self.show_presets) self.connectButton.clicked.connect(self.connect_pressed) self.update_file_tree() self.listButton.clicked.connect(self.list_mcu_files) self.mcuFilesListView.clicked.connect(self.mcu_file_selection_changed) self.mcuFilesListView.doubleClicked.connect(self.read_mcu_file) self.executeButton.clicked.connect(self.execute_mcu_code) self.removeButton.clicked.connect(self.remove_file) self.localPathEdit.setText(self._root_dir) local_selection_model = self.localFilesTreeView.selectionModel() local_selection_model.selectionChanged.connect(self.local_file_selection_changed) self.localFilesTreeView.doubleClicked.connect(self.open_local_file) # Set the "Name" column to always fit the content self.localFilesTreeView.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.compileButton.clicked.connect(self.compile_files) self.update_compile_button() self.autoTransferCheckBox.setChecked(Settings().auto_transfer) self.transferToMcuButton.clicked.connect(self.transfer_to_mcu) self.transferToPcButton.clicked.connect(self.transfer_to_pc) self.disconnected()
def update_compile_button(self): self.compileButton.setEnabled(bool(Settings().mpy_cross_path) and len(self.get_local_file_selection()) > 0)