Esempio n. 1
0
class Chip8Widget(QWidget):

    def __init__(self, machine: Machine, sound: bool=False, instruction_per_second: int = 500):
        super().__init__()
        self._machine = machine
        self.init_ui()
        self._sound = QSound('beep.wav')
        self._sound_support = sound

        self._machine_update_timer = Timer(interval=1.0 / instruction_per_second)
        self._machine_update_timer.add_handler(self._execute_instruction)
        self._machine_update_timer.start()

        self._machine_sound_delay_timer = Timer(interval=1.0 / 60)  # 60 Hz
        self._machine_sound_delay_timer.add_handler(self._update_sound_delay)
        self._machine_sound_delay_timer.start()

        self._key_dict = {
            Qt.Key_1: 1, Qt.Key_2: 2, Qt.Key_3: 3, Qt.Key_4: 0xC,
            Qt.Key_Q: 4, Qt.Key_W: 5, Qt.Key_E: 6, Qt.Key_R: 0xD,
            Qt.Key_A: 7, Qt.Key_S: 8, Qt.Key_D: 9, Qt.Key_F: 0xE,
            Qt.Key_Z: 0xA, Qt.Key_X: 0x0, Qt.Key_C: 0xB, Qt.Key_V: 0xF
        }

    def init_ui(self):
        screen = Chip8ScreenWidget(self._machine.Screen, self)
        self.setWindowTitle('Chip 8')
        self.show()

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if event.key() in self._key_dict:
            self._machine.Keyboard.key_down(self._key_dict[event.key()])

    def keyReleaseEvent(self, event: QtGui.QKeyEvent):
        if event.key() in self._key_dict:
            self._machine.Keyboard.key_up(self._key_dict[event.key()])

    def _execute_instruction(self):
        self._machine.execute_next_instruction()

    def _update_sound_delay(self):
        if self._machine.SoundTimer.get_count() != 0 and self._sound_support:
            self._sound.play()
        self._machine.DelayTimer.decrease()
        self._machine.SoundTimer.decrease()
Esempio n. 2
0
class Chip8ScreenWidget(QWidget):
    def __init__(self, screen: Screen, parent: QWidget = None):
        super().__init__(parent)
        self._screen = screen
        screen_size = QDesktopWidget().screenGeometry(-1)
        self._pixel_width = int(screen_size.width() * 1 / 100)
        self._pixel_height = self._pixel_width

        self.init_ui()

        self._draw_timer = Timer(interval=1.0 / 30)
        self._draw_timer.add_handler(self._draw_screen_event)
        self._draw_timer.start()

    def init_ui(self):
        self.setFixedSize(self._screen.width() * self._pixel_width,
                          self._screen.height() * self._pixel_height)
        self.show()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        self._draw_screen(qp)
        qp.end()

    def keyPressEvent(self, a0: QtGui.QKeyEvent):
        self.parent().keyPressEvent(a0)

    def _draw_screen_event(self):
        self.update()

    def _draw_screen(self, qp):
        qp.setPen(QColor(0, 0, 0))
        for y in range(self._screen.height()):
            for x in range(self._screen.width()):
                self._draw_pixel(y, x, qp)

    def _draw_pixel(self, y, x, qp):
        qp.fillRect(
            x * self._pixel_width, y * self._pixel_height, self._pixel_width,
            self._pixel_height,
            QColor(0, 0, 0) if self._screen.get_pixel(y, x) == 0 else QColor(
                255, 255, 255))
class Chip8MachineStateWidget(QWidget):
    def __init__(self, machine: Machine, parent: QWidget=None):
        super().__init__(parent)
        self._machine = machine

        self._init_ui()

        self._draw_timer = Timer(interval=1.0 / 30)
        self._draw_timer.add_handler(self._draw_state_event)
        self._draw_timer.start()

        self._instruction_factory = InstructionFactory()

    def _init_ui(self):
        screen_size = QDesktopWidget().screenGeometry(-1)
        self._pixel_width = int(screen_size.width() * 1 / 100)
        self._pixel_height = self._pixel_width

        self.setFixedHeight(self._pixel_height * 32)
        self.setFixedWidth(self._pixel_width * 35)

        self._text_size = int(self._pixel_height * 0.7)

        self.show()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)

        self._draw_state(qp)

        qp.end()

    def _draw_state_event(self):
        self.update()

    def keyPressEvent(self, a0: QtGui.QKeyEvent):
        self.parent().keyPressEvent(a0)

    def _draw_state(self, qp):
        qp.setFont(QFont('Noto Sans', self._text_size))

        t = self._text_size // 2

        qp.drawText(0, self._text_size, 'V: {}'.format(list(map(to_hex, self._machine.VRegisters))))
        qp.drawText(0, 2 * self._text_size + t, 'PC: {}, I: {}, DT: {}, ST: {}'
                    .format(to_hex(self._machine.PC),
                            to_hex(self._machine.AddressRegister),
                            to_hex(self._machine.DelayTimer.get_count()),
                            to_hex(self._machine.SoundTimer.get_count())))
        qp.drawText(0, 3 * self._text_size + 2 * t, 'Stack: {}'.format(list(map(to_hex, self._machine.Stack.items()))))
        qp.drawText(0, 4 * self._text_size + 3 * t, 'Memory at 10-radius of PC:')

        y = 5 * self._text_size + 4 * t

        for i in range(-10, 11):
            index = self._machine.PC + i * 2

            if index < 0 or index > self._machine.MemorySize:
                qp.drawText(0, y, '')
                continue

            opcode = self._get_opcode_at(index)

            try:
                instruction = self._instruction_factory.from_opcode(opcode)

                qp.drawText(0, y, '{}{} : {}, {} {} {}'
                            .format('-> ' if i == 0 else '    ',
                                    to_hex(index),
                                    list(map(to_hex, opcode)),
                                    instruction.__class__.__name__,
                                    list((map(to_hex, instruction.arg_registers))),
                                    to_hex(instruction.arg_constant) if instruction.arg_constant is not None else '-'))
            except InstructionFactoryError:
                qp.drawText(0, y, '{}{} : {}, [unknown]'
                            .format('-> ' if i == 0 else '    ',
                                    to_hex(index),
                                    list(map(to_hex, opcode))))

            y += self._text_size + t

        qp.drawText(0, y, 'Machine blocked' if self._machine.Block else 'Machine not blocked')

    def _get_opcode_at(self, i):
        return bytearray([self._machine.Memory[i], self._machine.Memory[i + 1]])
Esempio n. 4
0
class ProgressBar:
    def __init__(self, size, length=40):
        if length <= 2:
            raise ValueError('percent line length must be greater than 2')

        self.__size = size
        self.__current = 0
        self.__timer = Timer()
        self.__max_len = 1
        self.__length = length

    @property
    def length(self):
        return self.__length

    @property
    def timer(self):
        return self.__timer

    @property
    def elapsed_time(self):
        return self.__timer.elapsed

    def append(self, size):
        self.__current += size

    @property
    def current_size(self):
        return self.__current

    @property
    def size(self):
        return self.__size

    @property
    def bar(self):
        speed = self.__current / (self.elapsed_time + 1e-6)

        if self.__size is not None:
            percent = self.__current / self.__size * 100

            true_length = self.__length - 2
            filled_count = int(percent / 100 * true_length)

            bar = '[{}] {:.1f}% Speed: {:.2f} KB/s'.format(
                '#' * filled_count + ' ' * (true_length - filled_count),
                percent, speed / 1024)
        else:
            bar = 'Progressing with speed {:.2f} KB/s...'.format(speed / 1024)
        self.__max_len = max((self.__max_len, len(bar)))

        return bar

    @property
    def clearing_string(self):
        return ' ' * self.__max_len

    @property
    def statistic(self):
        if self.__size:
            return '{} of {} bytes from {:.2f} seconds'.format(
                self.__current, self.__size, self.elapsed_time)

        return '{} bytes from {:.2f} second'.format(self.__current,
                                                    self.elapsed_time)

    def print_with_clearing(self, writer: Writer = None):

        if writer is None:
            return

        space = ' ' * self.__max_len
        bar = self.bar
        writer.write(f'\r{space}\r{bar}', end='')

    def __enter__(self):
        self.__timer.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.__timer.kill()