Esempio n. 1
0
class Installer:
    def __init__(self, output_widget: QTextEdit = None):

        # create install process
        self._output_widget = None
        self.process = QProcess()
        self.process.setProgram(sys.executable)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.readyReadStandardOutput.connect(self._on_stdout_ready)
        # setup process path
        env = QProcessEnvironment()
        combined_paths = os.pathsep.join(
            [site.getsitepackages()[0], env.systemEnvironment().value("PYTHONPATH")]
        )
        env.insert("PATH", QProcessEnvironment.systemEnvironment().value("PATH"))
        env.insert("PYTHONPATH", combined_paths)
        self.process.setProcessEnvironment(env)
        self.set_output_widget(output_widget)

    def set_output_widget(self, output_widget: QTextEdit):
        if output_widget:
            self._output_widget = output_widget
            self.process.setParent(output_widget)

    def _on_stdout_ready(self):
        if self._output_widget:
            text = self.process.readAllStandardOutput().data().decode()
            self._output_widget.append(text)

    def install(self, pkg_list):
        cmd = ["-m", "pip", "install", "--upgrade"]
        self.process.setArguments(cmd + pkg_list)
        if self._output_widget:
            self._output_widget.clear()
        self.process.start()
Esempio n. 2
0
    def save(self, url):
        self._saving = True
        self.savingChanged.emit()
        self._savingProgress = 0
        self.savingProgressChanged.emit()
        p = QProcess(self)
        p.setProcessChannelMode(QProcess.ForwardedErrorChannel)

        stdout_buffer = b""

        def ready_read_stdout():
            nonlocal stdout_buffer
            stdout_buffer += p.readAllStandardOutput().data()
            *messages, stdout_buffer = stdout_buffer.split(b"\n")
            for message in messages:
                progress = json.loads(messages[-1].decode())
                self._savingProgress = progress["fraction"]
                self.savingProgressChanged.emit()
        p.readyReadStandardOutput.connect(ready_read_stdout)

        def finished(status):
            self._saving = False
            self.savingChanged.emit()
            if status != 0:
                self.savingError.emit()
        p.finished.connect(finished)
        args = ["-c", "from djpdf.scans2pdf import main; main()",
                os.path.abspath(url.toLocalFile())]
        if self._verbose:
            args.append("--verbose")
        p.start(sys.executable, args)
        p.write(json.dumps([p._data for p in self._pages]).encode())
        p.closeWriteChannel()
Esempio n. 3
0
    def save(self, url):
        self._saving = True
        self.savingChanged.emit()
        self._savingProgress = 0
        self.savingProgressChanged.emit()
        p = QProcess(self)
        p.setProcessChannelMode(QProcess.ForwardedErrorChannel)

        stdout_buffer = b""

        def ready_read_stdout():
            nonlocal stdout_buffer
            stdout_buffer += p.readAllStandardOutput().data()
            *messages, stdout_buffer = stdout_buffer.split(b"\n")
            for message in messages:
                progress = json.loads(messages[-1].decode())
                self._savingProgress = progress["fraction"]
                self.savingProgressChanged.emit()
        p.readyReadStandardOutput.connect(ready_read_stdout)
        p.finished.connect(self._process_finished)
        args = ["-c", "from djpdf.scans2pdf import main; main()",
                os.path.abspath(url.toLocalFile())]
        if self._verbose:
            args.append("--verbose")
        p.start(sys.executable, args)
        self._process = p
        p.write(json.dumps([p._data for p in self._pages]).encode())
        p.closeWriteChannel()
Esempio n. 4
0
class SerialControl(QObject):
    sig_keyseq_pressed = Signal(str)
    sig_CPU_comout = Signal(int)
    sig_CPU_comin = Signal(str)
    sig_port_change = Signal(str)
    sig_button_pressed = Signal(int)
    sig_terminal_open = Signal(bool)
    sig_firmware_update = Signal(str)

    def __init__(self, cpu, monitor_frame, terminal_frame, usb_frame,
                 statusbar, config, sig_update, config_file_path):
        QObject.__init__(self)

        self.cpu = cpu
        self.monitor = monitor_frame
        self.terminal = terminal_frame
        self.statusbar = statusbar
        self.config = config
        self.ser_port = None
        self.sig_update = sig_update
        self.fd_thread = None
        self.fwth = None
        self.monitor_frame = monitor_frame
        self.usb_frame = usb_frame
        self.config_file_path = config_file_path

        # Connect signal
        self.sig_keyseq_pressed.connect(self.on_key_pressed)
        self.sig_CPU_comout.connect(self.on_comout)
        self.sig_CPU_comin.connect(self.on_comin)
        self.sig_port_change.connect(self.on_port_change)
        self.sig_button_pressed.connect(self.on_button_dispatch)
        self.sig_terminal_open.connect(self.on_terminal_open)
        self.sig_firmware_update.connect(self.on_firmware_update)

        self.monitor_frame.sig_keyseq_pressed = self.sig_keyseq_pressed
        self.monitor_frame.sig_button_pressed = self.sig_button_pressed
        self.cpu.sig_CPU_comout = self.sig_CPU_comout
        self.cpu.sig_CPU_comin = self.sig_CPU_comin
        self.usb_frame.usb_combo.sig_port_change = self.sig_port_change
        self.usb_frame.sig_button_pressed = self.sig_button_pressed
        self.usb_frame.sig_firmware_update = self.sig_firmware_update

        self.terminal.sig_terminal_open = self.sig_terminal_open

        # Disable fw flash button if udr2 binary is nor present
        self.udr2 = f"cli/udr2-{sys.platform}" if sys.platform != "win32" else "cli\\udr2-win32.exe"
        if not os.path.isfile(self.udr2):
            self.usb_frame.firmware_btn.setEnabled(False)
        self.init_serial()

    def init_serial(self, do_refresh=True):
        is_thread = InitSerialThread(self)
        is_thread.update_combo = self.usb_frame.usb_combo.set_ports
        is_thread.do_refresh = do_refresh
        is_thread.start()

    def init_OK(self):
        self.statusbar.sig_temp_message.emit("Serial port Initialized")

    #
    # Signal Handling
    #
    @Slot(str)
    def on_key_pressed(self, key):
        if self.cpu.rx is None and len(key) == 1:
            self.cpu.rx = key
            self.monitor_frame.serial_in.setText(key)

    @Slot(str)
    def on_comin(self, char):
        """Char is handled by CPU, we free the slot"""
        self.monitor_frame.serial_in.setText(" ")

    @Slot(int)
    def on_comout(self, byte):
        """Append the char to the console"""
        self.monitor_frame.append_serial_out(byte)

    @Slot(str)
    def on_port_change(self, port):
        self.config.set("serial", "port", port)
        if self.ser_port:
            self.ser_port.port = port
        with open(self.config_file_path, 'w') as configfile:
            self.config.write(configfile)
        self.init_serial(False)

    @Slot(int)
    def on_button_dispatch(self, btn_nbr):
        if btn_nbr == 0:
            self.to_digirule()
        elif btn_nbr == 1:
            self.from_digirule()
        elif btn_nbr == 2:
            self.init_serial()
        elif btn_nbr == 3:
            self.on_clear_button()

    @Slot(bool)
    def on_terminal_open(self, is_open):
        if is_open:
            # Terminal window is open : create the terminal serial thread
            self.statusbar.sig_temp_message.emit("open terminal thread")
            self.terminal.serth = SerialThread(self)  # Start serial thread
            self.terminal.serth.start()
        else:
            # Terminal window is closed : quit the terminal serial thread
            if self.terminal.serth:
                self.terminal.serth.running = False
                sleep(0.5)
                self.terminal.serth = None

    def on_clear_button(self):
        self.monitor_frame.clear()  # Clear the serial in/out content
        self.cpu.rx = None
        self.cpu.tx = None

    def to_digirule(self):
        if self.ser_port is None:
            self.statusbar.sig_temp_message.emit(
                "Error : No serial port configured")
        else:
            dump = ram2hex(self.cpu.ram)
            self.statusbar.sig_temp_message.emit("Dumpimg memory on port " +
                                                 self.ser_port.port)
            try:
                self.ser_port.open()
            except serial.serialutil.SerialException as ex:
                self.statusbar.sig_temp_message.emit(ex.strerror)
            else:
                for line in dump.splitlines():
                    self.ser_port.write(line.encode("utf-8"))
                    sleep(0.1)
                sleep(2)
                self.ser_port.close()
                self.statusbar.sig_temp_message.emit("Memory sent")

    def from_digirule(self):
        """Launch receive sequence in background"""
        if self.ser_port:
            self.statusbar.sig_temp_message.emit(
                "Waiting to receive Digirule on " + self.ser_port.port)
            self.fd_thread = FromDigiruleThread(self)
            self.fd_thread.start()
        else:
            self.init_serial()

    @Slot(str)
    def on_firmware_update(self, filepath):
        if self.ser_port:
            self.proc = QProcess(self)
            self.proc.readyReadStandardOutput.connect(self.stdoutReady)
            self.proc.readyReadStandardError.connect(self.stderrReady)

            if sys.platform == "win32":
                command = f'{self.udr2} --program {self.ser_port.port} < {filepath}'
                self.usb_frame.out.write(
                    "Firmware update started, please wait ")
                # displays running dots on windows to pretend it is not stalled
                self.bullshitTimer = QTimer()
                self.bullshitTimer.timeout.connect(self.stdoutBullshit)
                self.bullshitTimer.start(1000)
                self.proc.setProcessChannelMode(QProcess.MergedChannels)
                self.proc.start('cmd.exe', ['/c', command])
            else:
                command = f'{self.udr2} --program {self.ser_port.port} < "{filepath}"'
                self.bullshitTimer = None
                self.proc.start('bash', ['-c', command])
            # print(command)

    def stdoutBullshit(self):
        self.usb_frame.out.write(".")

    def stdoutReady(self):
        if self.bullshitTimer:
            self.usb_frame.out.write("\n")
            self.bullshitTimer.stop()

        text = str(self.proc.readAllStandardOutput())
        self.usb_frame.out.write(eval(text).decode('iso8859-1'))

    def stderrReady(self):
        text = str(self.proc.readAllStandardError())
        self.usb_frame.out.write(eval(text).decode('iso8859-1'))
Esempio n. 5
0
class QKonsol(QPlainTextEdit):

    userTextEntry = ""
    commandList = ["cd " + QDir.homePath()]
    length = 0
    history = -1

    def __init__(self, parent=None):
        super().__init__()
        self.setParent(parent)
        self.setWindowTitle(self.tr("Terminal"))
        self.setCursorWidth(7)
        self.setContextMenuPolicy(Qt.NoContextMenu)
        font = self.font()
        font.setFamily("Consolas")
        font.setPointSize(10)
        self.setFont(font)
        self.setUndoRedoEnabled(False)
        palette = QPalette()
        palette.setColor(QPalette.Base, Qt.black)
        palette.setColor(QPalette.Text, Qt.white)
        palette.setColor(QPalette.Highlight, Qt.white)
        palette.setColor(QPalette.HighlightedText, Qt.black)
        self.setFrameShape(QFrame.NoFrame)
        self.setPalette(palette)
        self.resize(720, 480)

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.setReadChannel(QProcess.StandardOutput)

        self.process.readyReadStandardOutput.connect(self.readStandartOutput)
        self.process.readyReadStandardError.connect(
            lambda: print(self.readAllStandartError()))
        self.cursorPositionChanged.connect(self.cursorPosition)
        self.textChanged.connect(self.whatText)

        if sys.platform == "win32":
            self.process.start("cmd.exe", [], mode=QProcess.ReadWrite)

        else:
            self.process.start(
                "bash", ["-i"],
                mode=QProcess.ReadWrite)  # bash -i interactive mode

    def readStandartOutput(self):
        if sys.platform == "win32":
            st = self.process.readAllStandardOutput().data().decode(
                str(ctypes.cdll.kernel32.GetConsoleOutputCP()))

        else:
            st = self.process.readAllStandardOutput().data().decode("utf-8")

        # print(repr(st), self.commandList)
        if not st.startswith(self.commandList[-1]):
            self.appendPlainText(st)

    def __line_end(self):
        if sys.platform == "win32":
            return "\r\n"

        elif sys.platform == "linux":
            return "\n"

        elif sys.platform == "darwin":
            return "\r"

    def keyPressEvent(self, event: QKeyEvent):

        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            if self.commandList[
                    -1] != self.userTextEntry and self.userTextEntry != "":
                self.commandList.append(self.userTextEntry)

            self.length = len(self.userTextEntry + self.__line_end())
            self.process.writeData(self.userTextEntry + self.__line_end(),
                                   self.length)
            self.userTextEntry = ""

        elif event.key() == Qt.Key_Backspace:
            if self.userTextEntry == "":
                return

            else:
                self.userTextEntry = self.userTextEntry[:-1]
                super().keyPressEvent(event)

        elif event.key() == Qt.Key_Up:
            if -len(self.commandList) < self.history:
                self.history -= 1
                print(self.commandList[self.history])
            return

        elif event.key() == Qt.Key_Down:
            if self.history < -1:
                self.history += 1
                print(self.commandList[self.history])
            return

        elif event.key() == Qt.Key_Delete:
            return

        elif event.modifiers() == Qt.ControlModifier:
            super().keyPressEvent(event)

        else:
            super().keyPressEvent(event)
            self.userTextEntry += event.text()

    def cursorPosition(self):
        pass

    # print(self.textCursor().position())

    def whatText(self):
        pass

    # print(self.blockCount())

    def insertFromMimeData(self, source):
        super().insertFromMimeData(source)
        self.userTextEntry += source.text()

    def mouseReleaseEvent(self, event):
        super().mousePressEvent(event)
        cur = self.textCursor()

        if event.button() == Qt.LeftButton:
            cur.movePosition(QTextCursor.End, QTextCursor.MoveAnchor, 1)
            self.setTextCursor(cur)

    def closeEvent(self, event):
        self.process.close()
Esempio n. 6
0
class Bitcoin(object):
    file: ConfigurationFile
    hard_drives: HardDrives
    process: QProcess
    software: BitcoinSoftware
    zmq_block_port: int
    zmq_tx_port: int

    def __init__(self, configuration_file_path: str):
        self.hard_drives = HardDrives()
        self.software = BitcoinSoftware()
        self.file = ConfigurationFile(configuration_file_path)
        self.running = False
        self.process = None

        log.debug('datadir', datadir=self.file['datadir'])

        if (self.file['datadir'] is None
                or not os.path.exists(self.file['datadir'])):
            self.autoconfigure_datadir()

        if 'bitcoin.conf' in os.listdir(self.file['datadir']):
            actual_conf_file = os.path.join(self.file['datadir'], 'bitcoin.conf')
            if configuration_file_path != actual_conf_file:
                log.info(
                    'datadir_redirect',
                    configuration_file_path=configuration_file_path,
                    actual_conf_file=actual_conf_file
                )
                self.file = ConfigurationFile(actual_conf_file)
                if (self.file['datadir'] is None
                        or not os.path.exists(self.file['datadir'])):
                    self.autoconfigure_datadir()

        if os.path.exists(os.path.join(self.file['datadir'], 'blocks')):
            if self.file['prune'] is None:
                self.set_prune(False)

        self.wallet_paths = self.get_wallet_paths()

        if self.file['server'] is None:
            self.file['server'] = True

        if self.file['disablewallet'] is None and not self.wallet_paths:
            self.file['disablewallet'] = True
        elif self.file['disablewallet'] is None and self.wallet_paths:
            self.file['disablewallet'] = False

        if self.file['timeout'] is None:
            self.file['timeout'] = 6000

        if self.file['rpcuser'] is None:
            self.file['rpcuser'] = '******'

        if self.file['rpcpassword'] is None:
            self.file['rpcpassword'] = get_random_password()

        if self.file['prune'] is None:
            should_prune = self.hard_drives.should_prune(self.file['datadir'],
                                                         has_bitcoin=True)
            self.set_prune(should_prune)

        self.zmq_block_port = get_zmq_port()
        self.zmq_tx_port = get_zmq_port()

        self.file['zmqpubrawblock'] = f'tcp://127.0.0.1:{self.zmq_block_port}'
        self.file['zmqpubrawtx'] = f'tcp://127.0.0.1:{self.zmq_tx_port}'

        # noinspection PyBroadException
        try:
            memory = psutil.virtual_memory()
            free_mb = round(memory.available / 1000000)
            free_mb -= int(free_mb * .3)
            self.file['dbcache'] = free_mb
        except:
            log.warning(
                'dbcache psutil.virtual_memory',
                exc_info=True
            )
            self.file['dbcache'] = 1000

        self.config_snapshot = self.file.snapshot.copy()
        self.file.file_watcher.fileChanged.connect(self.config_file_changed)

        self.process = QProcess()
        self.process.setProgram(self.software.bitcoind)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.setArguments(self.args)

    @property
    def network(self):
        if self.file['testnet']:
            return TESTNET
        return MAINNET

    def get_wallet_paths(self):
        exclude_files = {
            'addr.dat',
            'banlist.dat',
            'fee_estimates.dat',
            'mempool.dat',
            'peers.dat'
        }
        candidate_paths = []
        if self.file['testnet']:
            datadir = os.path.join(self.file['datadir'], 'testnet3')
            wallet_dir = self.file['test.walletdir']
            wallets = self.file['test.wallet']
        else:
            datadir = self.file['datadir']
            wallet_dir = self.file['main.walletdir']
            wallets = self.file['main.wallet']
        for file in os.listdir(datadir):
            if file not in exclude_files:
                path = os.path.join(datadir, file)
                candidate_paths.append(path)
        default_walletdir = os.path.join(datadir, 'wallets')
        if os.path.exists(default_walletdir):
            for file in os.listdir(default_walletdir):
                if file not in exclude_files:
                    candidate_paths.append(
                        os.path.join(default_walletdir, file))
        if wallet_dir is not None:
            for file in os.listdir(wallet_dir):
                if file not in exclude_files:
                    candidate_paths += os.path.join(
                        os.path.join(wallet_dir, file))
        dat_files = [f for f in candidate_paths if f.endswith('.dat')
                     and not f.startswith('blk')]
        dat_files = set(dat_files)
        wallet_paths = set(dat_files - exclude_files)
        if wallets is not None:
            if isinstance(wallets, list):
                for wallet in wallets:
                    wallet_paths.add(wallet)
            else:
                wallet_paths.add(wallets)
        return wallet_paths

    @property
    def node_port(self):
        if self.file['testnet']:
            custom_port = self.file['test.port']
        else:
            custom_port = self.file['main.port']
        if custom_port is not None:
            return custom_port
        if self.file['testnet']:
            return BITCOIN_TESTNET_PEER_PORT
        return BITCOIN_MAINNET_PEER_PORT

    @property
    def rpc_port(self):
        if self.file['testnet']:
            custom_port = self.file['test.rpcport']
        else:
            custom_port = self.file['main.rpcport']
        if custom_port is not None:
            return custom_port
        if self.file['testnet']:
            return BITCOIN_TESTNET_RPC_PORT
        return BITCOIN_MAINNET_RPC_PORT

    def set_prune(self, should_prune: bool = None):

        if should_prune is None:
            should_prune = self.hard_drives.should_prune(self.file['datadir'],
                                                         has_bitcoin=True)
        if should_prune:
            if self.file['testnet']:
                prune = TESTNET_PRUNE
            else:
                prune = MAINNET_PRUNE
            self.file['prune'] = prune
        else:
            self.file['prune'] = 0
        self.file['txindex'] = not should_prune

    def autoconfigure_datadir(self):
        default_datadir = BITCOIN_DATA_PATH[OPERATING_SYSTEM]
        big_drive = self.hard_drives.get_big_drive()
        default_is_big_enough = not self.hard_drives.should_prune(
            input_directory=default_datadir,
            has_bitcoin=True
        )
        default_is_biggest = self.hard_drives.is_default_partition(big_drive)
        log.info(
            'autoconfigure_datadir',
            default_is_big_enough=default_is_big_enough,
            default_is_biggest=default_is_biggest
        )
        if default_is_big_enough or default_is_biggest:
            self.file['datadir'] = default_datadir
            log.info(
                'autoconfigure_datadir',
                datadir=default_datadir
            )
            return

        if not self.hard_drives.should_prune(big_drive.mountpoint, False):
            datadir = os.path.join(big_drive.mountpoint, 'Bitcoin')
            self.file['datadir'] = datadir
            log.info(
                'autoconfigure_datadir',
                datadir=datadir
            )
            if not os.path.exists(self.file['datadir']):
                os.mkdir(self.file['datadir'])
        else:
            self.file['datadir'] = default_datadir
            log.info(
                'autoconfigure_datadir',
                datadir=default_datadir
            )

    @property
    def args(self) -> List[str]:
        return [f'-conf={self.file.path}']

    @property
    def bitcoin_cli(self) -> str:
        command = [
            f'"{self.software.bitcoin_cli}"',
            f'-conf="{self.file.path}"',
        ]
        return ' '.join(command)

    def config_file_changed(self):
        # Refresh config file
        self.file.file_watcher.blockSignals(True)
        self.file.populate_cache()
        self.file.file_watcher.blockSignals(False)
        if self.file['zmqpubrawblock']:
            self.zmq_block_port = int(self.file['zmqpubrawblock'].split(':')[-1])
        if self.file['zmqpubrawtx']:
            self.zmq_tx_port = int(self.file['zmqpubrawtx'].split(':')[-1])
        # Some text editors do not modify the file, they delete and replace the file
        # Check if file is still in file_watcher list of files, if not add back
        files_watched = self.file.file_watcher.files()
        if len(files_watched) == 0:
            self.file.file_watcher.addPath(self.file.path)

    @property
    def restart_required(self):
        old_config = self.config_snapshot.copy()
        new_config = self.file.snapshot

        # First check that both config files are still on the same network
        old_config_network = 'testnet' in old_config.keys()
        new_config_network = 'testnet' in new_config.keys()

        if (old_config_network == new_config_network) and self.running:
            common_fields = [
                'rpcuser', 'rpcpassword', 'disablewallet', 'datadir', 'disablewallet',
                'zmqpubrawblock', 'zmqpubrawtx', 'prune', 'txindex', 'timeout'
            ]

            for field in common_fields:

                # First check if field is found in both configs
                found_in_old_config = field in old_config.keys()
                found_in_new_config = field in new_config.keys()
                if found_in_old_config != found_in_new_config:
                    return True

                # Now check that values are the same
                if found_in_old_config:
                    if old_config[field] != new_config[field]:
                        return True

            if self.file['testnet']:
                # Only check testnet fields if currently running testnet
                testnet_fields = [
                    'test.rpcport', 'test.port', 'test.wallet', 'test.walletdir'
                ]
                for field in testnet_fields:
                    # First check if field is found in both configs
                    found_in_old_config = field in old_config.keys()
                    found_in_new_config = field in new_config.keys()

                    if found_in_old_config != found_in_new_config:
                        return True

                    # Now check that values are the same
                    if found_in_old_config:
                        if old_config[field] != new_config[field]:
                            return True

            else:
                # Only check mainnet fields if currently running mainnet
                mainnet_fields = [
                    'rpcport', 'port'
                ]

                for field in mainnet_fields:
                    # First check if field is found in both configs
                    found_in_old_config = field in old_config.keys()
                    found_in_new_config = field in new_config.keys()
                    if found_in_old_config != found_in_new_config:
                        return True

                    # Now check that values are the same
                    if found_in_old_config:
                        if old_config[field] != new_config[field]:
                            return True

            return False
        elif self.running:
            # Network has changed and the node is running - Restart is required
            return True

        return False