Ejemplo n.º 1
0
class SingleTask:
    """Class for working with single task of execution."""
    def __init__(self, button, task_func, parameters):
        """Class initialization.

        :param TwoStateButton button: button that activates task.
        :param task_func: function to execute.
        :param dict parameters: function's parameters.
        """
        self.run_and_stop_button = button
        self.task_func = task_func
        self.parameters = parameters
        self.threads = ThreadPool()
        self.process = None
        self.run_and_stop_button.connect_first_state(self.execute)
        self.run_and_stop_button.connect_second_state(self.abort)

    def execute(self):
        """Execute function in safe thread."""
        logger.debug(
            f"Executing single task: {self.__class__.__name__} {self.task_func.__name__}"
        )
        from lib.gui.widgets.main import MainWindow
        MainWindow.resume_recorder()
        worker = self.threads.run_thread(target=self._execute)
        worker.signals.finished.connect(
            self.run_and_stop_button.set_first_state)
        worker.signals.finished.connect(MainWindow.pause_recorder)
        self.run_and_stop_button.set_second_state()

    @safe_process_stop
    def abort(self):
        """Abort function's execution."""
        if self.process:
            logger.debug("Task was forcibly stopped.")
            self.process.terminate()
        self.threads.thread_pool.clear()
        self.run_and_stop_button.set_first_state()
        from lib.gui.widgets.main import MainWindow
        MainWindow.pause_recorder()

    def _execute(self):
        """Execute function."""
        self.process = Process(target=self.task_func, kwargs=self.parameters)
        self.process.start()
        self.process.join()
        logger.debug("Task completed.")
Ejemplo n.º 2
0
class SingleTaskWithOptions:
    def __init__(self, button, task_func, task_options):
        """Class initialization.

        :param TwoStateButton button: button that activates task.
        :param task_func: function to execute.
        :param dict task_options: function's parameters by option's key.
        """
        self.run_and_stop_button = button
        self.task_func = task_func
        self.task_options = task_options
        self.threads = ThreadPool()
        self.process = None
        for task_name, task_parameters in task_options.items():

            def add_action(parameters):
                self.run_and_stop_button.add_action(
                    task_name, lambda: self.execute(parameters))

            add_action(task_parameters)
        self.menu = self.run_and_stop_button.button.menu()
        self.run_and_stop_button.connect_second_state(self.abort)

    def execute(self, parameters):
        """Execute function in safe thread."""
        logger.debug(
            f"Executing single task: {self.__class__.__name__} {self.task_func.__name__} "
            f"with parameters {parameters}")
        from lib.gui.widgets.main import MainWindow
        MainWindow.resume_recorder()
        worker = self.threads.run_thread(
            target=lambda: self._execute(parameters=parameters))
        worker.signals.finished.connect(
            self.run_and_stop_button.set_first_state)
        worker.signals.finished.connect(self._set_menu)
        worker.signals.finished.connect(MainWindow.pause_recorder)
        self._clear_menu()
        self.run_and_stop_button.set_second_state()

    def _clear_menu(self):
        """Clear button menu."""
        self.run_and_stop_button.button.setMenu(None)

    def _set_menu(self):
        """Set button menu from cache."""
        self.run_and_stop_button.button.setMenu(self.menu)

    @safe_process_stop
    def abort(self):
        """Abort function's execution."""
        if self.process:
            logger.debug("Task was forcibly stopped.")
            self.process.terminate()
        self.threads.thread_pool.clear()
        self._set_menu()
        self.run_and_stop_button.set_first_state()
        from lib.gui.widgets.main import MainWindow
        MainWindow.pause_recorder()

    def _execute(self, parameters):
        """Execute function."""
        self.process = Process(target=self.task_func, kwargs=parameters)
        self.process.start()
        self.process.join()
        logger.debug("Task completed.")
Ejemplo n.º 3
0
class QueueList:
    """Class for working with queue list."""

    def __init__(self, game, list_widget, run_and_stop_button, add_button, edit_button, remove_button,
                 queue_selector_buttons):
        """Class initialization.

        :param PyQt5.QtWidgets.QListWidget.QListWidget list_widget: list widget.
        :param lib.gui.helper.TwoStateButton run_and_stop_button: button for running the queue.
        :param PyQt5.QtWidgets.QPushButton.QPushButton add_button: button for adding new element to queue.
        :param PyQt5.QtWidgets.QPushButton.QPushButton edit_button: button for editing existing element in the queue.
        :param PyQt5.QtWidgets.QPushButton.QPushButton remove_button:
            button for removing existing element from the queue.
        :param list[PyQt5.QtWidgets.QPushButton.QPushButton] queue_selector_buttons:
            list of buttons for selecting different queues.
        """
        self.game = game
        self.widget = list_widget
        self.run_and_stop_button = run_and_stop_button
        self.add_button = add_button
        self.edit_button = edit_button
        self.remove_button = remove_button
        self.queue_selector_buttons = queue_selector_buttons
        self.stored_queues = [[], ] * len(self.queue_selector_buttons)  # type: List[List[QueueItem]]
        self.current_queue_index = 0
        self.setup_buttons()
        self.threads = ThreadPool()
        self.process = None
        self.add_button.clicked.connect(self.add)
        if self.widget.count() == 0:
            self.run_and_stop_button.button.setEnabled(False)
        self.select_all_item = self.add_select_all_checkbox()
        self.widget.itemChanged.connect(self.on_item_change)
        self.load_queue_from_file()
        self.change_select_all_state()
        self.stop_queue_flag = False

    def queue(self):
        """Queue iterator."""
        for i in range(self.widget.count()):
            item = self.widget.item(i)
            if isinstance(item, QueueItem):
                yield item

    @property
    def queue_fifo(self):
        """Creates FIFO representation of current queue.

        :rtype: deque[QueueItem]
        """
        return deque(list(self.queue()))

    def clear_queue(self):
        """Clears queue."""
        for _ in range(self.widget.count()):
            item = self.widget.item(0)
            self.widget.takeItem(self.widget.row(item))

    def store_current_queue(self):
        """Stores currently selected queue to variable."""
        self.stored_queues[self.current_queue_index] = [*self.queue()]

    def change_queue(self, index):
        """Changes queue by index.

        :param int index: queue's index.
        """
        if index != self.current_queue_index:
            self.store_current_queue()
        self.current_queue_index = index
        self.clear_queue()
        self.select_all_item = self.add_select_all_checkbox()
        for item in self.stored_queues[index]:
            self._add(item)
        for button in self.queue_selector_buttons:
            button.setChecked(False)
        self.queue_selector_buttons[index].setChecked(True)

    def add_select_all_checkbox(self):
        """Creates 'Select All' checkbox with empty line below."""
        select_all = QListWidgetItem()
        select_all.setText("[Select All]")
        select_all.setCheckState(Qt.Checked)
        select_all.setFlags(select_all.flags() | Qt.ItemIsUserCheckable)
        select_all.setFlags(select_all.flags() ^ Qt.ItemIsDragEnabled)
        select_all.setFlags(select_all.flags() ^ Qt.ItemIsSelectable)
        blank_line = QListWidgetItem()
        blank_line.setFlags(blank_line.flags() ^ Qt.ItemIsDragEnabled)
        blank_line.setFlags(blank_line.flags() ^ Qt.ItemIsSelectable)
        self.widget.addItem(select_all)
        self.widget.addItem(blank_line)
        return select_all

    def change_select_all_state(self):
        """Changes 'Select All' checkbox state by queue item's states."""
        queue_states = [queue_item.checkState() for queue_item in self.queue()]
        all_checked = [state for state in queue_states if state == Qt.Checked]
        all_unchecked = [state for state in queue_states if state == Qt.Unchecked]
        partially_checked = all_checked and all_unchecked
        if all_checked and not all_unchecked:
            self.select_all_item.setCheckState(Qt.Checked)
        if all_unchecked and not all_checked:
            self.select_all_item.setCheckState(Qt.Unchecked)
        if partially_checked:
            self.select_all_item.setCheckState(Qt.PartiallyChecked)

    def on_item_change(self, item):
        """Selects or deselects items when some item was checked.

        :param QListWidgetItem item: changed item.
        """
        if item == self.select_all_item:
            state = item.checkState()
            if state == Qt.Checked:
                self.select_all()
            if state == Qt.Unchecked:
                self.deselect_all()
        if isinstance(item, QueueItem):
            if len(self.widget.selectedItems()) > 1:
                for selected_item in self.widget.selectedItems():
                    selected_item.setCheckState(item.checkState())
            self.change_select_all_state()

    def load_queue_from_file(self):
        """Loads queue list and apply it to GUI."""
        queues_list = load_queue_list()
        if not queues_list:
            return
        for queue_index, queue in enumerate(queues_list):
            logger.debug(f"Loading {len(queue)} items to queue list #{queue_index + 1}.")
            queue_items = []
            for settings in queue:
                editor = QueueItemEditor.from_settings(game=self.game, settings=settings)
                if editor:
                    item = editor.render_queue_item()
                    item.set_checked(settings.get("checked", False))
                    queue_items.append(item)
            self.stored_queues[queue_index] = queue_items
        self.change_queue(index=0)

    def save_queue_to_file(self):
        """Saves existing queue to JSON-file."""
        self.store_current_queue()
        queues_list = []
        for queue_index, queue in enumerate(self.stored_queues):
            queue_items = []
            for item in queue:
                settings = {
                    "mode_name": item.mode_name,
                    "checked": item.is_checked,
                    **item.parameters
                }
                queue_items.append(settings)
            queues_list.append(queue_items)
            logger.debug(f"Saving queue #{queue_index + 1} list with {len(queue)} items.")
        save_queue_list(queues_list)

    def setup_buttons(self):
        """Setups button's events."""
        self.run_and_stop_button.connect_first_state(self.run_queue)
        self.run_and_stop_button.connect_second_state(self.stop_queue)
        self.run_and_stop_button.connect_first_state(self.widget.setDragDropMode, QAbstractItemView.NoDragDrop)
        self.run_and_stop_button.connect_second_state(self.widget.setDragDropMode, QAbstractItemView.InternalMove)
        self.remove_button.clicked.connect(self.remove_current_item)
        self.edit_button.clicked.connect(self.edit_current_item)

        # Setup Queue #1 and etc. buttons to change queue
        def change_queue_on_click(button, queue_index):
            button.clicked.connect(lambda: self.change_queue(queue_index))

        for index in range(len(self.queue_selector_buttons)):
            change_queue_on_click(button=self.queue_selector_buttons[index], queue_index=index)

    def add(self):
        """Creates editor window and add queue item from it."""
        editor = QueueItemEditor(game=self.game)
        editor.setWindowTitle("Add queue item")
        result = editor.exec_()
        if result and editor.queue_item:
            self._add(editor.queue_item)

    def _add(self, item):
        """Adds item to queue.

        :param QListWidgetItem item: item to add.
        """
        if self.widget.count() == 2:
            self.run_and_stop_button.button.setEnabled(True)
        self.widget.addItem(item)
        self.change_select_all_state()
        return item

    def edit_current_item(self):
        """Edits current selected item."""
        item = self.widget.currentItem()
        if item and isinstance(item, QueueItem):
            editor = QueueItemEditor.from_result_item(game=self.game, queue_item=item)
            editor.setWindowTitle("Edit queue item")
            result = editor.exec_()
            if result and editor.queue_item:
                self.edit_item(old_item=item, new_item=editor.queue_item)

    def edit_item(self, old_item, new_item):
        """Edits queue item.

        :param QListWidgetItem old_item: item before editing.
        :param QListWidgetItem new_item: item after editing.
        """
        widget_index = self.widget.row(old_item)
        self.widget.takeItem(widget_index)
        self.widget.insertItem(widget_index, new_item)
        self.widget.setCurrentRow(widget_index)

    def remove_current_item(self):
        """Removes current selected item from queue."""
        item = self.widget.currentItem()
        if item and isinstance(item, QueueItem):
            if len(self.widget.selectedItems()) > 1:
                for selected_item in self.widget.selectedItems():
                    self.remove_item(selected_item)
            self.remove_item(item)

    def remove_item(self, item):
        """Removes item from queue.

        :param QListWidgetItem item: queue item.
        """
        self.widget.takeItem(self.widget.row(item))
        self.change_select_all_state()
        if self.widget.count() == 2:
            self.run_and_stop_button.button.setEnabled(False)

    def add_queue_by_index(self, queue, queue_index):
        """Adds items from queue to current queue by queue index.

        :param deque[QueueItem] queue: current FIFO queue.
        :param queue_index: index of queue to add.
        """
        logger.debug(f"Running queue by index = {queue_index}")
        for item in reversed(self.stored_queues[queue_index - 1]):
            clone = item.clone(mark=True)
            queue.appendleft(clone)

    def run_queue(self):
        """Runs and executes all items in queue."""
        logger.debug("Running queue.")
        self.store_current_queue()
        from lib.gui.widgets.main import MainWindow
        MainWindow.resume_recorder()
        self.run_and_stop_button.set_second_state()
        self.widget.setDragDropMode(QAbstractItemView.NoDragDrop)
        self.threads.run_thread(func=self._run_queue,
                                on_finish=[self.run_and_stop_button.set_first_state,
                                           self.reset_background,
                                           MainWindow.pause_recorder],
                                on_progress=self.mark_execution_background,
                                with_progress=True)

    def mark_execution_background(self, cur_index):
        """Marks execution queue items with color.

        :param int cur_index: index for current item in queue.
        """
        for index, item in enumerate(self.queue()):
            if index == cur_index:
                item.setBackground(Qt.yellow)
                break
            color = Qt.green if item.is_checked else Qt.gray
            item.setBackground(color)

    def reset_background(self):
        """Resets queue colors."""
        for item in self.queue():
            item.setBackground(Qt.transparent)

    @safe_process_stop
    def stop_queue(self):
        """Stops queue execution."""
        from lib.gui.widgets.main import MainWindow
        MainWindow.pause_recorder()
        self.game.clear_modes()
        self.widget.setDragDropMode(QAbstractItemView.InternalMove)
        self.stop_queue_flag = True
        if self.process:
            logger.debug("Queue was forcibly stopped.")
            self.process.terminate()
        self.threads.thread_pool.clear()
        self.run_and_stop_button.set_first_state()

    def _run_queue(self, progress_callback):
        """Runs item's execution.

        :param PyQt5.QtCore.pyqtSignal.pyqtSignal progress_callback: signal to emit queue progress.
        """
        queue = self.queue_fifo
        index = -1
        while queue:
            item = queue.popleft()
            if not item.was_cloned:
                index += 1
            if self.stop_queue_flag:
                break
            progress_callback.emit(index)
            executor, settings = item.get_executor()
            if item.mode_name == "RUN QUEUE" and item.is_checked:
                self.add_queue_by_index(queue=queue, **settings)
                continue
            if not executor:
                logger.debug(f"Skipping queue item: {item.mode_name}")
                continue
            logger.debug(f"Running {item.mode_name} with settings: {settings}")
            self.process = Process(target=executor, kwargs=settings)
            self.process.start()
            self.process.join()
        self.stop_queue_flag = False
        self.widget.setDragDropMode(QAbstractItemView.InternalMove)
        self.game.clear_modes()
        logger.debug("Queue completed.")

    def select_all(self):
        """Selects all items in queue."""
        for item in self.queue():
            item.set_checked(True)

    def deselect_all(self):
        """Deselects all items in queue."""
        for item in self.queue():
            item.set_checked(False)