コード例 #1
0
ファイル: __init__.py プロジェクト: octorock/the-little-hat
class ConstraintCleanerPlugin:
    name = 'Constraint Cleaner'
    description = 'Cleans up duplicate constraints\nand disables redundant constraints'
    hidden = True

    def __init__(self, api: PluginApi) -> None:
        self.api = api

    def load(self) -> None:
        #self.action_remove_duplicate = self.api.register_menu_entry('Remove duplicate constraints', self.slot_remove_duplicate)
        self.action_remove_redundant = self.api.register_menu_entry(
            'Remove redundant constraints', self.slot_remove_redundant)

    def unload(self) -> None:
        #self.api.remove_menu_entry(self.action_remove_duplicate)
        self.api.remove_menu_entry(self.action_remove_redundant)

    def slot_remove_duplicate(self) -> None:
        # TODO
        pass

    def slot_remove_redundant(self) -> None:
        '''
        Disables all constraints that only contain redundant information and don't create more relations
        '''

        progress_dialog = self.api.get_progress_dialog(
            'Constraint Cleaner', 'Removing redundant constraints...', False)
        progress_dialog.show()

        self.thread = QThread()
        self.worker = RemoveRedundantWorker()
        self.worker.moveToThread(self.thread)

        self.worker.signal_progress.connect(
            lambda progress: progress_dialog.set_progress(progress))
        self.worker.signal_done.connect(
            lambda: (  # https://stackoverflow.com/a/13672943
                self.thread.quit(), progress_dialog.close(),
                QMessageBox.information(
                    self.api.main_window, 'Constraint Cleaner',
                    'All redundant constraints are removed.')))
        self.worker.signal_fail.connect(lambda: (
            self.thread.quit(), progress_dialog.close(),
            QMessageBox.critical(
                self.api.main_window, 'Constraint Cleaner',
                'Failed to add a constraint.\nSee console for more information.'
            )))

        self.thread.started.connect(self.worker.process)
        self.thread.start()
コード例 #2
0
                if filename[-3:] == 'qml' and 'IN_MODIFY' in type_names:
                    reload = True
                    break

            if reload:
                self.requestReload.emit()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    workerThread = QThread()
    workerThread.start()
    worker = Worker()
    worker.moveToThread(workerThread)

    master = Master()
    master.command.connect(worker.run)
    worker.requestReload.connect(master.reload)
    master.command.emit()

    # Stop application gracefully:
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    status = app.exec_()
    worker.stop()
    workerThread.quit()
    workerThread.wait()
    sys.exit(status)
コード例 #3
0
class GameDisplay(QWidget):
    default_font = 'Sans Serif,9,-1,5,50,0,0,0,0,0'
    rules_path: typing.Optional[str] = None

    move_needed = Signal(int, np.ndarray)  # active_player, board
    move_made = Signal(np.ndarray)  # board
    game_ended = Signal(np.ndarray)  # final_board

    def __init__(self, start_state: GameState):
        super().__init__()
        self.start_state = start_state
        self.mcts_workers: typing.Dict[int, MctsWorker] = {}
        self.worker_thread: typing.Optional[QThread] = None
        self.current_state = self.start_state
        self.valid_moves = self.start_state.get_valid_moves()
        self._show_coordinates = False
        self.log_display = LogDisplay()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.is_reviewing = False

    @property
    def show_coordinates(self):
        return self._show_coordinates

    @show_coordinates.setter
    def show_coordinates(self, value):
        self._show_coordinates = value
        scene = self.scene()
        size = QSize(scene.width(), scene.height())
        self.resizeEvent(QResizeEvent(size, size))

    @property
    def mcts_players(self):
        return [worker.player for worker in self.mcts_workers.values()]

    @mcts_players.setter
    def mcts_players(self, players: typing.Sequence[MctsPlayer]):
        self.stop_workers()

        self.log_display = LogDisplay()
        self.mcts_workers = {player.player_number: MctsWorker(player)
                             for player in players}
        if not self.mcts_workers:
            self.worker_thread = None
        else:
            self.worker_thread = QThread()
            for worker in self.mcts_workers.values():
                worker.move_chosen.connect(self.make_move)  # type: ignore
                worker.move_analysed.connect(self.analyse_move)  # type: ignore
                # noinspection PyUnresolvedReferences
                self.move_needed.connect(worker.choose_move)  # type: ignore
                # noinspection PyUnresolvedReferences
                self.move_made.connect(worker.analyse_move)  # type: ignore
                worker.moveToThread(self.worker_thread)
            self.worker_thread.start()

    def get_player(self, player_number: int) -> typing.Optional[MctsPlayer]:
        worker = self.mcts_workers.get(player_number)
        if worker:
            return worker.player
        return None

    @abstractmethod
    def update_board(self, board: GameState):
        """ Update self.scene, based on the state in board.

        It's probably also helpful to override resizeEvent().

        :param board: the state of the game to display.
        """

    def resizeEvent(self, event: QResizeEvent):
        self.update_board(self.current_state)

    @property
    def credit_pairs(self) -> typing.Iterable[typing.Tuple[str, str]]:
        """ Return a list of label and detail pairs.

        These are displayed in the about box.
        """
        return ()

    def choose_active_text(self):
        active_player = self.current_state.get_active_player()
        if active_player in self.mcts_workers:
            return 'thinking'
        return 'to move'

    @Slot(int)  # type: ignore
    def make_move(self, move: int):
        self.log_display.record_move(self.current_state, move)
        # noinspection PyUnresolvedReferences
        self.move_made.emit(self.current_state)  # type: ignore
        self.current_state = self.current_state.make_move(move)
        self.update_board(self.current_state)
        if self.current_state.is_ended():
            # noinspection PyUnresolvedReferences
            self.game_ended.emit(self.current_state)  # type: ignore

        forced_move = self.get_forced_move()
        if forced_move is None:
            self.request_move()
        else:
            self.make_move(forced_move)

    def get_forced_move(self) -> typing.Optional[int]:
        """ Override this method if some moves should be forced.

        Look at self.valid_moves and self.current_board to decide.
        :return: move number, or None if there is no forced move.
        """
        return None

    @Slot(GameState, int, list)  # type: ignore
    def analyse_move(
            self,
            board: GameState,
            analysing_player: int,
            move_probabilities: typing.List[typing.Tuple[str,
                                                         float,
                                                         int,
                                                         float]]):
        self.log_display.analyse_move(board,
                                      analysing_player,
                                      move_probabilities)

    def request_move(self):
        if self.current_state.is_ended():
            return
        player = self.current_state.get_active_player()
        # noinspection PyUnresolvedReferences
        self.move_needed.emit(player, self.current_state)

    def close(self):
        self.stop_workers()

    def stop_workers(self):
        if self.worker_thread is not None:
            self.worker_thread.quit()

    def can_move(self):
        if self.is_reviewing:
            return False
        return not self.current_state.get_active_player() in self.mcts_workers
コード例 #4
0
ファイル: __init__.py プロジェクト: octorock/the-little-hat
class ShiftabilityTesterPlugin:
    name = 'Shiftability Tester'
    description = 'Tests whether a rom with .space inside it was\nshifted correctly.'
    hidden = True

    def __init__(self, api: PluginApi) -> None:
        self.api = api
        self.locations = []

    def load(self) -> None:
        self.action_test_shiftability = self.api.register_menu_entry(
            'Test Shiftability', self.slot_test_shiftability)
        self.action_next_location = self.api.register_menu_entry(
            'Next Location', self.slot_next_location)
        self.action_next_location.setShortcut(QKeySequence(Qt.Key_F4))

    def unload(self) -> None:
        self.api.remove_menu_entry(self.action_test_shiftability)
        self.api.remove_menu_entry(self.action_next_location)

    def slot_test_shiftability(self) -> None:
        progress_dialog = self.api.get_progress_dialog(
            'Shiftability Tester', 'Testing shiftability...', False)
        progress_dialog.show()

        self.thread = QThread()
        self.worker = TestShiftabilityWorker()
        self.worker.moveToThread(self.thread)

        self.worker.signal_progress.connect(
            lambda progress: progress_dialog.set_progress(progress))
        self.worker.signal_done.connect(
            lambda: (self.thread.quit(), progress_dialog.close(),
                     self.api.show_message(
                         'Shiftability Tester',
                         'Test complete. See console for more information.')))
        self.worker.signal_fail.connect(
            lambda message: (self.thread.quit(), progress_dialog.close(
            ), self.api.show_error('Shiftability Tester', message)))
        self.worker.signal_locations.connect(self.slot_set_locations)

        self.thread.started.connect(self.worker.process)
        self.thread.start()

    def slot_set_locations(self, locations) -> None:
        self.locations = locations

    def slot_next_location(self) -> None:
        if len(self.locations) == 0:
            self.api.show_error(
                'Shiftability Tester',
                'Shiftability not tested yet or all locations visited.')
            return
        location = self.locations.pop(
            0) - 2  # as we shift by 0x10000, this should be moved

        # TODO add this in better to the plugin api: find linked usa controller

        controller = None
        for contrl in self.api.main_window.dock_manager.hex_viewer_manager.controllers:
            if contrl.rom_variant == RomVariant.USA and contrl.is_linked == True:
                controller = contrl
                break
        if controller is None:
            self.api.show_error('Shiftability Tester',
                                'Need a USA hex viewer that is linked')
            return

        controller.update_cursor(
            controller.address_resolver.to_virtual(location))
コード例 #5
0
ファイル: __init__.py プロジェクト: stefs/evelyn-reminder
class EvelynDesktop(QStackedWidget):
    INTERVAL_SECS = 30
    ALERT_SECS = 5

    signal_get_ping = Signal()
    signal_post_history = Signal(int, QDateTime)

    def __init__(
            self,
            config_file: str
    ) -> None:
        super().__init__()
        # load config
        try:
            self.config = Config(config_file)
        except Exception as e:
            QMessageBox.critical(self, 'Config error', str(e))
            QTimer.singleShot(0, self.close)
            return
        # load settings
        self.settings = Settings()
        # state
        self.state_key: Optional[int] = None
        # label widget
        self.label_ping = ClickableLabel('Loading ...', self.post_history)
        self.label_ping.setTextFormat(Qt.RichText)
        self.label_ping.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        layout_ping = QGridLayout()
        layout_ping.setContentsMargins(0, 0, 0, 0)
        layout_ping.addWidget(self.label_ping)
        self.widget_ping = QWidget()
        self.widget_ping.setLayout(layout_ping)
        self.addWidget(self.widget_ping)
        # alert widget
        self.label_alert = QLabel()
        self.label_alert.setWordWrap(True)
        self.label_alert.setAlignment(Qt.AlignCenter)
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.addWidget(self.label_alert)
        # context menu
        self.action_report_done = QAction('Report done ...')
        self.action_report_done.triggered.connect(self.report_done)
        self.action_exit = QAction('Exit')
        self.action_exit.triggered.connect(self.close)
        self.action_frameless = QAction('Frameless window')
        self.action_frameless.setCheckable(True)
        self.action_frameless.triggered.connect(self.set_frameless_window)
        self.action_homepage = QAction('Open homepage')
        self.action_homepage.triggered.connect(self.open_homepage)
        self.context_menu = QMenu()
        self.context_menu.addAction(self.action_report_done)
        self.context_menu.addAction(self.action_exit)
        self.context_menu.addAction(self.action_frameless)
        self.context_menu.addAction(self.action_homepage)
        # threads
        self.thread_communication = QThread()
        self.thread_communication.start()
        # workers
        self.worker_communication = CommunicationWorker(
            netloc=self.config.netloc,
            base_path=self.config.base_path,
            api_key=self.config.api_key,
            guild=self.config.guild,
            member=self.config.member)
        self.worker_communication.moveToThread(self.thread_communication)
        # signals
        self.worker_communication.signal_get_ping_done.connect(self.get_ping_done)
        self.worker_communication.signal_post_history_done.connect(self.post_history_done)
        self.signal_get_ping.connect(self.worker_communication.get_ping)
        self.signal_post_history.connect(self.worker_communication.post_history)
        # get ping timer
        QTimer.singleShot(0, self.get_ping)
        self.timer_ping = QTimer()
        self.timer_ping.timeout.connect(self.get_ping)
        self.timer_ping.setTimerType(Qt.VeryCoarseTimer)
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        # switch label timer
        self.timer_label = QTimer()
        self.timer_label.timeout.connect(lambda: self.setCurrentWidget(self.widget_ping))
        self.timer_label.setSingleShot(True)
        self.timer_label.setTimerType(Qt.CoarseTimer)
        # window attributes
        size = self.settings.get('window', 'size', type_=QSize)
        if size is not None:
            self.resize(size)
        pos = self.settings.get('window', 'pos', type_=QPoint)
        if pos is not None:
            self.move(pos)
        frameless = self.settings.get('window', 'frameless', type_=bool)
        if frameless is not None and frameless:
            QTimer.singleShot(100, self.action_frameless.trigger)
        self.setWindowFlag(Qt.WindowStaysOnTopHint, self.config.window_stays_on_top)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowTitle('Evelyn Reminder')

    def closeEvent(
            self,
            event: QCloseEvent
    ) -> None:
        # save settings
        with suppress_and_log_exception():
            self.settings.set('window', 'size', self.size())
            self.settings.set('window', 'pos', self.pos())
            self.settings.set('window', 'frameless', bool(self.windowFlags() & Qt.FramelessWindowHint))
        # stop communication thread
        with suppress_and_log_exception():
            self.thread_communication.quit()
            self.thread_communication.wait()
        # done
        super().closeEvent(event)

    def contextMenuEvent(
            self,
            event: QContextMenuEvent
    ) -> None:
        self.context_menu.exec_(event.globalPos())

    @Slot()
    def get_ping(self) -> None:
        logging.info('Get ping ...')
        self.signal_get_ping.emit()

    @Slot(int, str, str)
    def get_ping_done(
            self,
            key: int,
            text: str,
            color: str
    ) -> None:
        logging.info('Get ping done')
        if key == -1:
            self.state_key = None
            self.label_ping.setWordWrap(True)
        else:
            self.state_key = key
            self.label_ping.setWordWrap(False)
        self.label_ping.setText(text)
        self.widget_ping.setStyleSheet(f'background : {color}; ')

    @Slot()
    def post_history(
            self,
            date_time: QDateTime = QDateTime()
    ) -> None:
        # this method is called as Slot by ClickableLabel.mouseReleaseEvent() without arguments
        # this method is called directly by EvelynDesktop.report_done() with a date_time
        if self.state_key is None:
            return
        logging.info('Post history ...')
        self.label_alert.setText('Sending ...')
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.setCurrentWidget(self.label_alert)
        self.signal_post_history.emit(self.state_key, date_time)

    @Slot(str, bool)
    def post_history_done(
            self,
            text: str,
            error: bool
    ) -> None:
        logging.info('Post history done')
        self.label_alert.setText(text)
        if error:
            self.label_alert.setStyleSheet(f'background: #dd4b4b;')
        self.timer_label.start(self.ALERT_SECS * 1000)
        # trigger instant ping update to avoid outdated info
        self.timer_ping.stop()
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        self.get_ping()

    @Slot()
    def report_done(self) -> None:
        self.timer_ping.stop()  # stop ping update while dialog is open
        report_done_dialog = ReportDoneDialog(self)
        response = report_done_dialog.exec()
        if response != QDialog.Accepted:
            self.timer_ping.start(self.INTERVAL_SECS * 1000)
            self.get_ping()
            return
        date_time = report_done_dialog.get_date_time()
        self.post_history(date_time)

    @Slot(bool)
    def set_frameless_window(
            self,
            value: bool
    ) -> None:
        pos = self.pos()
        self.setWindowFlag(Qt.FramelessWindowHint, value)
        # workaround: window goes invisible otherwise
        self.setVisible(True)
        # workaround: window would move up otherwise
        if value:
            QTimer.singleShot(100, lambda: self.move(pos))

    @Slot()
    def open_homepage(self) -> None:
        webbrowser.open('https://github.com/stefs/evelyn-reminder')