from argparse import Namespace from collections import deque from enum import IntEnum from logging import getLogger from typing import Optional, List, Deque from PyQt5.QtCore import pyqtSignal, pyqtSlot from PyQt5.QtWidgets import ( QWidget, QGroupBox, QVBoxLayout, QLabel, QSizePolicy, ) from legendary.models.game import Game, InstalledGame from rare.components.tabs.downloads.widgets import QueueWidget, UpdateWidget from rare.models.install import InstallOptionsModel, InstallQueueItemModel logger = getLogger("QueueGroup") class UpdateGroup(QGroupBox): enqueue = pyqtSignal(InstallOptionsModel) def __init__(self, parent=None): super(UpdateGroup, self).__init__(parent=parent) self.setObjectName(type(self).__name__) self.setTitle(self.tr("Updates")) self.__text = QLabel(self.tr("No updates available")) self.__text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) # lk: For findChildren to work, the upates's layout has to be in a widget self.__container = QWidget(self) self.__container.setLayout(QVBoxLayout()) self.__container.layout().setContentsMargins(0, 0, 0, 0) self.setLayout(QVBoxLayout()) self.layout().addWidget(self.__text) self.layout().addWidget(self.__container) self.__list: List[str] = [] @staticmethod def __widget_name(app_name: str) -> str: return f"UpdateWidget_{app_name}" def __find_widget(self, app_name: str) -> Optional[UpdateWidget]: return self.__container.findChild(UpdateWidget, name=self.__widget_name(app_name)) def count(self) -> int: return len(self.__list) def contains(self, app_name: str) -> bool: if app_name in self.__list: return self.__find_widget(app_name) is not None else: return False def append(self, game: Game, igame: InstalledGame): self.__text.setVisible(False) self.__container.setVisible(True) self.__list.append(game.app_name) widget: UpdateWidget = self.__find_widget(game.app_name) if widget is not None: self.__container.layout().removeWidget(widget) widget.deleteLater() widget = UpdateWidget(game, igame, parent=self.__container) widget.enqueue.connect(self.enqueue) self.__container.layout().addWidget(widget) def remove(self, app_name: str): self.__list.remove(app_name) widget: UpdateWidget = self.__find_widget(app_name) self.__container.layout().removeWidget(widget) widget.deleteLater() self.__text.setVisible(not bool(self.count())) self.__container.setVisible(bool(self.count())) def set_widget_enabled(self, app_name: str, enabled: bool): widget: UpdateWidget = self.__find_widget(app_name) widget.set_enabled(enabled) def get_update_version(self, app_name: str) -> str: widget: UpdateWidget = self.__find_widget(app_name) return widget.version() class QueueGroup(QGroupBox): removed = pyqtSignal(str) force = pyqtSignal(InstallQueueItemModel) def __init__(self, parent=None): super(QueueGroup, self).__init__(parent=parent) self.setObjectName(type(self).__name__) self.setTitle(self.tr("Queue")) self.__text = QLabel(self.tr("No downloads in queue"), self) self.__text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) # lk: For findChildren to work, the queue's layout has to be in a widget self.__container = QWidget(self) self.__container.setLayout(QVBoxLayout()) self.__container.layout().setContentsMargins(0, 0, 0, 0) self.__container.setVisible(False) self.setLayout(QVBoxLayout()) self.layout().addWidget(self.__text) self.layout().addWidget(self.__container) self.__queue: Deque[str] = deque() @staticmethod def __widget_name(app_name:str) -> str: return f"QueueWidget_{app_name}" def __find_widget(self, app_name: str) -> Optional[QueueWidget]: return self.__container.findChild(QueueWidget, name=self.__widget_name(app_name)) def contains(self, app_name: str) -> bool: if app_name in self.__queue: return self.__find_widget(app_name) is not None else: return False def count(self) -> int: return len(self.__queue) def __create_widget(self, item: InstallQueueItemModel, old_igame: InstalledGame) -> QueueWidget: widget: QueueWidget = QueueWidget(item, old_igame, parent=self.__container) widget.toggle_arrows(self.__queue.index(item.download.game.app_name), len(self.__queue)) widget.remove.connect(self.remove) widget.force.connect(self.__on_force) widget.move_up.connect(self.__on_move_up) widget.move_down.connect(self.__on_move_down) return widget def push_front(self, item: InstallQueueItemModel, old_igame: InstalledGame): self.__text.setVisible(False) self.__container.setVisible(True) self.__queue.appendleft(item.download.game.app_name) widget = self.__create_widget(item, old_igame) self.__container.layout().insertWidget(0, widget) if self.count() > 1: app_name = self.__queue[1] other: QueueWidget = self.__find_widget(app_name) other.toggle_arrows(1, len(self.__queue)) def push_back(self, item: InstallQueueItemModel, old_igame: InstalledGame): self.__text.setVisible(False) self.__container.setVisible(True) self.__queue.append(item.download.game.app_name) widget = self.__create_widget(item, old_igame) self.__container.layout().addWidget(widget) if self.count() > 1: app_name = self.__queue[-2] other: QueueWidget = self.__find_widget(app_name) other.toggle_arrows(len(self.__queue) - 2, len(self.__queue)) def pop_front(self) -> InstallQueueItemModel: app_name = self.__queue.popleft() widget: QueueWidget = self.__find_widget(app_name) item = widget.item widget.deleteLater() self.__text.setVisible(not bool(self.count())) self.__container.setVisible(bool(self.count())) return item def __update_queue(self): """ check the first, second, last and second to last widgets in the list and update their arrows :return: None """ for idx in [0, 1]: if self.count() > idx: app_name = self.__queue[idx] widget: QueueWidget = self.__find_widget(app_name) widget.toggle_arrows(idx, len(self.__queue)) for idx in [1, 2]: if self.count() > idx: app_name = self.__queue[-idx] widget: QueueWidget = self.__find_widget(app_name) widget.toggle_arrows(len(self.__queue) - idx, len(self.__queue)) def __remove(self, app_name: str): self.__queue.remove(app_name) widget: QueueWidget = self.__find_widget(app_name) self.__container.layout().removeWidget(widget) widget.deleteLater() self.__update_queue() self.__text.setVisible(not bool(self.count())) self.__container.setVisible(bool(self.count())) @pyqtSlot(str) def remove(self, app_name: str): self.__remove(app_name) self.removed.emit(app_name) @pyqtSlot(InstallQueueItemModel) def __on_force(self, item: InstallQueueItemModel): self.__remove(item.options.app_name) self.force.emit(item) class MoveDirection(IntEnum): UP = -1 DOWN = 1 def __move(self, app_name: str, direction: MoveDirection): """ Moved the widget for `app_name` up or down in the queue and the container :param app_name: The app_name associated with the widget :param direction: -1 to move up, +1 to move down :return: None """ index = self.__queue.index(app_name) self.__queue.remove(app_name) self.__queue.insert(index + int(direction), app_name) widget: QueueWidget = self.__find_widget(app_name) self.__container.layout().insertWidget(index + int(direction), widget) self.__update_queue() @pyqtSlot(str) def __on_move_up(self, app_name: str): self.__move(app_name, QueueGroup.MoveDirection.UP) @pyqtSlot(str) def __on_move_down(self, app_name: str): self.__move(app_name, QueueGroup.MoveDirection.DOWN)