1
0
Fork 0
mirror of synced 2024-06-26 10:11:19 +12:00

Add ImageWidget and LibraryWidget from #196

Add the Image and Library widgets from #196. In this iteration they replace the image `QLabel` in the existing widgets.

The `PaintWidget` in the `InstallingWidget` has been replaced by the future `LibraryWidget` that has progress indication.

The `ImageWidget` was also used to replace the image `QLabel` in `GameInfo` and `GameDlc` widgets.

Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
This commit is contained in:
loathingKernel 2022-06-19 20:42:49 +03:00
parent 8888cb3aee
commit 6335293eef
14 changed files with 337 additions and 211 deletions

View file

@ -87,7 +87,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
logger.warning("No Unreal engine in library found") logger.warning("No Unreal engine in library found")
self.ue_name = "" self.ue_name = ""
self.uninstalled_info_tabs = UninstalledInfoTabs(self.ue_name, self) self.uninstalled_info_tabs = UninstalledInfoTabs(self)
self.uninstalled_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0)) self.uninstalled_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
self.addWidget(self.uninstalled_info_tabs) self.addWidget(self.uninstalled_info_tabs)

View file

@ -1,5 +1,4 @@
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QPixmap, QResizeEvent
from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
from legendary.models.game import Game from legendary.models.game import Game
@ -9,6 +8,7 @@ from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallOptionsModel
from rare.widgets.image_widget import ImageWidget
class GameDlc(QWidget, Ui_GameDlc): class GameDlc(QWidget, Ui_GameDlc):
@ -94,13 +94,15 @@ class GameDlcWidget(QFrame, Ui_GameDlcWidget):
self.setupUi(self) self.setupUi(self)
self.dlc = dlc self.dlc = dlc
self.image.setFixedSize(ImageSize.Smaller.size) self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Smaller)
self.dlc_layout.insertWidget(0, self.image)
self.dlc_name.setText(dlc.app_title) self.dlc_name.setText(dlc.app_title)
self.version.setText(dlc.app_version()) self.version.setText(dlc.app_version())
self.app_name.setText(dlc.app_name) self.app_name.setText(dlc.app_name)
self.pixmap = self.image_manager.get_pixmap(dlc.app_name) self.image.setPixmap(self.image_manager.get_pixmap(dlc.app_name))
if installed: if installed:
self.action_button.setProperty("uninstall", 1) self.action_button.setProperty("uninstall", 1)
@ -111,26 +113,6 @@ class GameDlcWidget(QFrame, Ui_GameDlcWidget):
self.action_button.clicked.connect(self.install_game) self.action_button.clicked.connect(self.install_game)
self.action_button.setText(self.tr("Install DLC")) self.action_button.setText(self.tr("Install DLC"))
def resizeEvent(self, a0: QResizeEvent) -> None:
self.image.clear()
super(GameDlcWidget, self).resizeEvent(a0)
self.setPixmap(self.pixmap)
def setPixmap(self, a0: QPixmap) -> None:
self.pixmap = a0
self.image.setPixmap(
self.pixmap.scaledToHeight(
self.dlc_info.size().height()
- (
self.image.contentsMargins().top()
+ self.image.contentsMargins().bottom()
+ self.image.lineWidth() * 2
),
Qt.SmoothTransformation,
)
)
self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
def uninstall_dlc(self): def uninstall_dlc(self):
self.action_button.setDisabled(True) self.action_button.setDisabled(True)
self.action_button.setText(self.tr("Uninstalling")) self.action_button.setText(self.tr("Uninstalling"))

View file

@ -40,6 +40,7 @@ from rare.utils.legendary_utils import VerifyWorker
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallOptionsModel
from rare.utils.steam_grades import SteamWorker from rare.utils.steam_grades import SteamWorker
from rare.utils.utils import get_size from rare.utils.utils import get_size
from rare.widgets.image_widget import ImageWidget
logger = getLogger("GameInfo") logger = getLogger("GameInfo")
@ -60,6 +61,10 @@ class GameInfo(QWidget, Ui_GameInfo):
self.image_manager = ImageManagerSingleton() self.image_manager = ImageManagerSingleton()
self.game_utils = game_utils self.game_utils = game_utils
self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Display)
self.layout_game_info.insertWidget(0, self.image, alignment=Qt.AlignTop)
if platform.system() == "Windows": if platform.system() == "Windows":
self.lbl_grade.setVisible(False) self.lbl_grade.setVisible(False)
self.grade.setVisible(False) self.grade.setVisible(False)
@ -283,11 +288,7 @@ class GameInfo(QWidget, Ui_GameInfo):
self.igame = self.core.get_installed_game(self.game.app_name) self.igame = self.core.get_installed_game(self.game.app_name)
self.title.setTitle(self.game.app_title) self.title.setTitle(self.game.app_title)
pixmap = self.image_manager.get_pixmap(self.game.app_name) self.image.setPixmap(self.image_manager.get_pixmap(self.game.app_name, color=True))
if pixmap.isNull():
pixmap = self.image_manager.get_pixmap(self.parent().parent().parent().ue_name)
pixmap = pixmap.scaled(ImageSize.Display.size)
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name) self.app_name.setText(self.game.app_name)
if self.igame: if self.igame:

View file

@ -17,16 +17,17 @@ from rare.utils.extra_widgets import SideTabWidget
from rare.utils.json_formatter import QJsonModel from rare.utils.json_formatter import QJsonModel
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallOptionsModel
from rare.utils.steam_grades import SteamWorker from rare.utils.steam_grades import SteamWorker
from rare.widgets.image_widget import ImageWidget
class UninstalledInfoTabs(SideTabWidget): class UninstalledInfoTabs(SideTabWidget):
def __init__(self, ue_default_name, parent=None): def __init__(self, parent=None):
super(UninstalledInfoTabs, self).__init__(show_back=True, parent=parent) super(UninstalledInfoTabs, self).__init__(show_back=True, parent=parent)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton() self.signals = GlobalSignalsSingleton()
self.args = ArgumentsSingleton() self.args = ArgumentsSingleton()
self.info = UninstalledInfo(ue_default_name) self.info = UninstalledInfo()
self.info.install_button.setDisabled(self.args.offline) self.info.install_button.setDisabled(self.args.offline)
self.addTab(self.info, self.tr("Information")) self.addTab(self.info, self.tr("Information"))
@ -70,7 +71,7 @@ class GameMetadataView(QTreeView):
class UninstalledInfo(QWidget, Ui_GameInfo): class UninstalledInfo(QWidget, Ui_GameInfo):
game: Game game: Game
def __init__(self, ue_default_name, parent=None): def __init__(self, parent=None):
super(UninstalledInfo, self).__init__(parent=parent) super(UninstalledInfo, self).__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
@ -78,6 +79,10 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.api_results = ApiResultsSingleton() self.api_results = ApiResultsSingleton()
self.image_manager = ImageManagerSingleton() self.image_manager = ImageManagerSingleton()
self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Display)
self.layout_game_info.insertWidget(0, self.image, alignment=Qt.AlignTop)
self.install_button.clicked.connect(self.install_game) self.install_button.clicked.connect(self.install_game)
if platform.system() != "Windows": if platform.system() != "Windows":
self.steam_worker = SteamWorker(self.core) self.steam_worker = SteamWorker(self.core)
@ -96,7 +101,6 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.game_actions_stack.setCurrentIndex(1) self.game_actions_stack.setCurrentIndex(1)
self.game_actions_stack.resize(self.game_actions_stack.minimumSize()) self.game_actions_stack.resize(self.game_actions_stack.minimumSize())
self.lbl_platform.setText(self.tr("Platforms")) self.lbl_platform.setText(self.tr("Platforms"))
self.ue_default_name = ue_default_name
def install_game(self): def install_game(self):
self.signals.install_game.emit(InstallOptionsModel(app_name=self.game.app_name)) self.signals.install_game.emit(InstallOptionsModel(app_name=self.game.app_name))
@ -111,11 +115,7 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
available_platforms.append("macOS") available_platforms.append("macOS")
self.platform.setText(", ".join(available_platforms)) self.platform.setText(", ".join(available_platforms))
pixmap = self.image_manager.get_pixmap(game.app_name, color=False) self.image.setPixmap(self.image_manager.get_pixmap(self.game.app_name, color=True))
if pixmap.isNull():
pixmap = self.image_manager.get_pixmap(self.ue_default_name, color=False)
pixmap = pixmap.scaled(ImageSize.Display.size)
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name) self.app_name.setText(self.game.app_name)
self.version.setText(self.game.app_version("Windows")) self.version.setText(self.game.app_version("Windows"))

View file

@ -4,12 +4,13 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction, QLabel from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
from rare.components.tabs.games.game_utils import GameUtils from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.utils.utils import create_desktop_link from rare.utils.utils import create_desktop_link
from rare.widgets.image_widget import ImageWidget
logger = getLogger("Game") logger = getLogger("Game")
@ -63,11 +64,9 @@ class BaseInstalledWidget(QGroupBox):
except AttributeError: except AttributeError:
pass pass
self.image = QLabel() self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Display.size) self.image.setFixedSize(ImageSize.Display)
self.image.setPixmap( self.image.setPixmap(pixmap)
pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)
)
self.game_running = False self.game_running = False
self.offline = self.args.offline self.offline = self.args.offline
self.update_available = False self.update_available = False
@ -144,7 +143,7 @@ class BaseInstalledWidget(QGroupBox):
def reload_image(self): def reload_image(self):
self.image_manager.download_image_blocking(self.game, force=True) self.image_manager.download_image_blocking(self.game, force=True)
pm = self.image_manager.get_pixmap(self.game.app_name, color=True) pm = self.image_manager.get_pixmap(self.game.app_name, color=True)
self.image.setPixmap(pm.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)) self.image.setPixmap(pm)
def create_desktop_link(self, type_of_link): def create_desktop_link(self, type_of_link):
if type_of_link == "desktop": if type_of_link == "desktop":

View file

@ -1,10 +1,11 @@
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QGroupBox, QLabel, QAction from PyQt5.QtWidgets import QGroupBox, QAction
from legendary.models.game import Game from legendary.models.game import Game
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.widgets.image_widget import ImageWidget
logger = getLogger("Uninstalled") logger = getLogger("Uninstalled")
@ -21,9 +22,9 @@ class BaseUninstalledWidget(QGroupBox):
self.game.app_title = f"{self.game.app_title} {self.game.app_name.split('_')[-1]}" self.game.app_title = f"{self.game.app_title} {self.game.app_name.split('_')[-1]}"
self.core = core self.core = core
self.image = QLabel() self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.Display.size) self.image.setFixedSize(ImageSize.Display)
self.image.setPixmap(pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)) self.image.setPixmap(pixmap)
self.installing = False self.installing = False
self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@ -35,7 +36,7 @@ class BaseUninstalledWidget(QGroupBox):
def reload_image(self): def reload_image(self):
self.image_manager.download_image_blocking(self.game, force=True) self.image_manager.download_image_blocking(self.game, force=True)
pm = self.image_manager.get_pixmap(self.game.app_name, color=False) pm = self.image_manager.get_pixmap(self.game.app_name, color=False)
self.image.setPixmap(pm.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)) self.image.setPixmap(pm)
def install(self): def install(self):
self.show_uninstalled_info.emit(self.game) self.show_uninstalled_info.emit(self.game)

View file

@ -1,41 +1,46 @@
from PyQt5.QtCore import Qt, QRect from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPaintEvent, QPainter, QPixmap, QPen, QFont, QColor from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox, QWidget
from legendary.models.game import Game from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton from rare.shared import LegendaryCoreSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.utils.utils import ( from rare.widgets.elide_label import ElideLabel
optimal_text_background, from .library_widget import LibraryWidget
text_color_for_background,
)
class InstallingGameWidget(QWidget): class InstallingGameWidget(QGroupBox):
game: Game = None game: Game = None
def __init__(self): def __init__(self):
super(InstallingGameWidget, self).__init__() super(InstallingGameWidget, self).__init__()
layout = QVBoxLayout()
self.setObjectName("game_widget_icon") self.setObjectName("game_widget_icon")
self.setProperty("noBorder", 1) self.setContentsMargins(0, 0, 0, 0)
self.setLayout(QVBoxLayout())
self.core = LegendaryCoreSingleton() self.core = LegendaryCoreSingleton()
self.image_manager = ImageManagerSingleton()
self.pixmap = QPixmap() self.pixmap = QPixmap()
self.image_widget = PaintWidget() self.image = LibraryWidget(parent=self)
self.setContentsMargins(0, 0, 0, 0) self.image.setFixedSize(ImageSize.Display)
self.image_widget.setFixedSize(ImageSize.Display.size) layout.addWidget(self.image)
self.layout().addWidget(self.image_widget)
self.title_label = QLabel(f"<h4>Error</h4>") miniwidget = QWidget(self)
self.title_label.setAutoFillBackground(False) miniwidget.setFixedWidth(ImageSize.Display.size.width())
self.title_label.setWordWrap(True)
self.title_label.setFixedWidth(175)
minilayout = QHBoxLayout() minilayout = QHBoxLayout()
self.setObjectName("game_widget") minilayout.setContentsMargins(0, 0, 0, 0)
minilayout.addWidget(self.title_label) minilayout.setSpacing(0)
self.layout().addLayout(minilayout) miniwidget.setLayout(minilayout)
self.title_label = ElideLabel(f"<h4>Error</h4>", parent=miniwidget)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label, stretch=2)
minilayout.setAlignment(Qt.AlignTop)
layout.addWidget(miniwidget)
self.setLayout(layout)
def set_game(self, app_name): def set_game(self, app_name):
if not app_name: if not app_name:
@ -43,73 +48,11 @@ class InstallingGameWidget(QWidget):
return return
self.game = self.core.get_game(app_name) self.game = self.core.get_game(app_name)
self.title_label.setText(f"<h4>{self.game.app_title}</h4>") self.title_label.setText(f"<h4>{self.game.app_title}</h4>")
self.image.hideProgress(True)
self.image_widget.set_game(self.game.app_name) self.image.showProgress(
self.image_manager.get_pixmap(app_name, color=True),
self.image_manager.get_pixmap(app_name, color=False),
)
def set_status(self, s: int): def set_status(self, s: int):
self.image_widget.progress = s self.image.updateProgress(s)
self.image_widget.repaint()
class PaintWidget(QWidget):
color_image: QPixmap
bw_image: QPixmap
progress: int = 0
def __init__(self):
super(PaintWidget, self).__init__()
self.core = LegendaryCoreSingleton()
self.image_manager = ImageManagerSingleton()
def set_game(self, app_name: str):
game = self.core.get_game(app_name, False)
self.color_image = self.image_manager.get_pixmap(game.app_name, color=False)
self.color_image = self.color_image.scaled(
ImageSize.Display.size, transformMode=Qt.SmoothTransformation
)
self.setFixedSize(ImageSize.Display.size)
self.bw_image = self.image_manager.get_pixmap(app_name, color=False)
self.bw_image = self.bw_image.scaled(
ImageSize.Display.size, transformMode=Qt.SmoothTransformation
)
self.progress = 0
pixel_list = []
for x in range(self.color_image.width()):
for y in range(self.color_image.height()):
# convert pixmap to qimage, get pixel and remove alpha channel
pixel_list.append(
self.color_image.toImage().pixelColor(x, y).getRgb()[:-1]
)
self.rgb_tuple = optimal_text_background(pixel_list)
def paintEvent(self, a0: QPaintEvent) -> None:
painter = QPainter()
painter.begin(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.drawPixmap(self.rect(), self.bw_image)
w = self.bw_image.width() * self.progress // 100
painter.drawPixmap(
0,
0,
w,
self.color_image.height(),
self.color_image.copy(QRect(0, 0, w, self.color_image.height())),
)
# Draw Circle
pen = QPen(QColor(*self.rgb_tuple), 3)
painter.setPen(pen)
painter.setBrush(QColor(*self.rgb_tuple))
painter.drawEllipse(
int(self.width() / 2) - 20, int(self.height() / 2) - 20, 40, 40
)
# draw text
painter.setPen(QColor(*text_color_for_background(self.rgb_tuple)))
painter.setFont(QFont(None, 16))
painter.drawText(a0.rect(), Qt.AlignCenter, f"{self.progress}%")
painter.end()

View file

@ -0,0 +1,124 @@
from typing import Optional, Tuple, List
from PyQt5.QtCore import Qt, QRect, QEvent
from PyQt5.QtGui import QPainter, QPixmap, QResizeEvent, QFontMetrics, QImage, QBrush, QColor
from PyQt5.QtWidgets import QLabel
from rare.widgets.image_widget import ImageWidget
class ProgressLabel(QLabel):
def __init__(self, parent):
super(ProgressLabel, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
self.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.setFrameStyle(QLabel.StyledPanel)
def setGeometry(self, a0: QRect) -> None:
fm = QFontMetrics(self.font())
rect = fm.boundingRect(f" {self.text()} ")
rect.moveCenter(a0.center())
super(ProgressLabel, self).setGeometry(rect)
@staticmethod
def calculateColors(image: QImage) -> Tuple[QColor, QColor]:
color: List[int] = [0, 0, 0]
# take the two diagonals of the center square section
min_d = min(image.width(), image.height())
origin_w = (image.width() - min_d) // 2
origin_h = (image.height() - min_d) // 2
for x, y in zip(range(origin_w, min_d), range(origin_h, min_d)):
pixel = image.pixelColor(x, y).getRgb()
color = list(map(lambda t: sum(t) // 2, zip(pixel[0:3], color)))
# take the V component of the HSV color
fg_color = QColor(0, 0, 0) if QColor(*color).value() < 127 else QColor(255, 255, 255)
bg_color = QColor(*map(lambda c: 255 - c, color))
return bg_color, fg_color
def setStyleSheetColors(self, bg: QColor, fg: QColor, brd: QColor):
sheet = (
f"background-color: rgba({bg.red()}, {bg.green()}, {bg.blue()}, 65%);"
f"color: rgb({fg.red()}, {fg.green()}, {fg.blue()});"
f"border-width: 1px;"
f"border-radius: 5%;"
f"border-color: rgb({brd.red()}, {brd.green()}, {brd.blue()});"
f"font-weight: bold;"
f"font-size: 16pt;"
)
self.setStyleSheet(sheet)
class LibraryWidget(ImageWidget):
_color_pixmap: Optional[QPixmap] = None
_gray_pixmap: Optional[QPixmap] = None
# lk: keep percentage to not over-generate the image
_progress: int = -1
def __init__(
self,
parent=None,
) -> None:
super(LibraryWidget, self).__init__(parent)
self.progress_label = ProgressLabel(self)
self.progress_label.setVisible(False)
def event(self, e: QEvent) -> bool:
if e.type() == QEvent.LayoutRequest:
self.progress_label.setGeometry(self.rect())
return super(LibraryWidget, self).event(e)
def resizeEvent(self, a0: QResizeEvent) -> None:
if self.progress_label.isVisible():
self.progress_label.setGeometry(self.rect())
super(LibraryWidget, self).resizeEvent(a0)
def progressPixmap(self, color: QPixmap, gray: QPixmap, progress: int) -> QPixmap:
"""
Paints the color image over the gray images based on progress percentage
@param color:
@param gray:
@param progress:
@return:
"""
device = QPixmap(color.size())
painter = QPainter(device)
painter.setRenderHint(QPainter.SmoothPixmapTransform, self._smooth_transform)
painter.setCompositionMode(QPainter.CompositionMode_Source)
# lk: Vertical loading
# prog_h = (device.height() * progress // 100)
# brush = QBrush(gray)
# painter.fillRect(device.rect().adjusted(0, 0, 0, -prog_h), brush)
# brush.setTexture(color)
# painter.fillRect(device.rect().adjusted(0, device.height() - prog_h, 0, 0), brush)
# lk: Horizontal loading
prog_w = device.width() * progress // 100
brush = QBrush(gray)
painter.fillRect(device.rect().adjusted(prog_w, 0, 0, 0), brush)
brush.setTexture(color)
painter.fillRect(device.rect().adjusted(0, 0, prog_w - device.width(), 0), brush)
painter.end()
device.setDevicePixelRatio(color.devicePixelRatioF())
return device
def showProgress(self, color_pm: QPixmap, gray_pm: QPixmap) -> None:
self._color_pixmap = color_pm
self._gray_pixmap = gray_pm
bg_color, fg_color = self.progress_label.calculateColors(color_pm.toImage())
self.progress_label.setStyleSheetColors(bg_color, fg_color, fg_color)
self.progress_label.setVisible(True)
self.progress_label.update()
self.updateProgress(0)
def updateProgress(self, progress: int):
if progress > self._progress:
self._progress = progress
self.progress_label.setText(f"{progress:02}%")
self.setPixmap(self.progressPixmap(self._color_pixmap, self._gray_pixmap, progress))
def hideProgress(self, stopped: bool):
self._color_pixmap = None
self._gray_pixmap = None
self.progress_label.setVisible(stopped)
self._progress = -1

View file

@ -25,12 +25,6 @@ class Ui_GameDlcWidget(object):
GameDlcWidget.setFrameShadow(QtWidgets.QFrame.Plain) GameDlcWidget.setFrameShadow(QtWidgets.QFrame.Plain)
self.dlc_layout = QtWidgets.QHBoxLayout(GameDlcWidget) self.dlc_layout = QtWidgets.QHBoxLayout(GameDlcWidget)
self.dlc_layout.setObjectName("dlc_layout") self.dlc_layout.setObjectName("dlc_layout")
self.image = QtWidgets.QLabel(GameDlcWidget)
self.image.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.image.setFrameShadow(QtWidgets.QFrame.Plain)
self.image.setText("image")
self.image.setObjectName("image")
self.dlc_layout.addWidget(self.image)
self.dlc_info = QtWidgets.QWidget(GameDlcWidget) self.dlc_info = QtWidgets.QWidget(GameDlcWidget)
self.dlc_info.setObjectName("dlc_info") self.dlc_info.setObjectName("dlc_info")
self.dlc_info_layout = QtWidgets.QFormLayout(self.dlc_info) self.dlc_info_layout = QtWidgets.QFormLayout(self.dlc_info)
@ -105,7 +99,7 @@ class Ui_GameDlcWidget(object):
self.action_button.setText("Action") self.action_button.setText("Action")
self.action_button.setObjectName("action_button") self.action_button.setObjectName("action_button")
self.dlc_layout.addWidget(self.action_button, 0, QtCore.Qt.AlignTop) self.dlc_layout.addWidget(self.action_button, 0, QtCore.Qt.AlignTop)
self.dlc_layout.setStretch(2, 1) self.dlc_layout.setStretch(1, 1)
self.retranslateUi(GameDlcWidget) self.retranslateUi(GameDlcWidget)
QtCore.QMetaObject.connectSlotsByName(GameDlcWidget) QtCore.QMetaObject.connectSlotsByName(GameDlcWidget)

View file

@ -25,20 +25,7 @@
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
</property> </property>
<layout class="QHBoxLayout" name="dlc_layout" stretch="0,0,1,0"> <layout class="QHBoxLayout" name="dlc_layout" stretch="0,1,0">
<item>
<widget class="QLabel" name="image">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string notr="true">image</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QWidget" name="dlc_info" native="true"> <widget class="QWidget" name="dlc_info" native="true">
<layout class="QFormLayout" name="dlc_info_layout"> <layout class="QFormLayout" name="dlc_info_layout">

View file

@ -17,12 +17,6 @@ class Ui_GameInfo(object):
GameInfo.resize(791, 583) GameInfo.resize(791, 583)
self.layout_game_info = QtWidgets.QHBoxLayout(GameInfo) self.layout_game_info = QtWidgets.QHBoxLayout(GameInfo)
self.layout_game_info.setObjectName("layout_game_info") self.layout_game_info.setObjectName("layout_game_info")
self.image = QtWidgets.QLabel(GameInfo)
self.image.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.image.setFrameShadow(QtWidgets.QFrame.Sunken)
self.image.setText("")
self.image.setObjectName("image")
self.layout_game_info.addWidget(self.image, 0, QtCore.Qt.AlignTop)
self.layout_game_info_form = QtWidgets.QGridLayout() self.layout_game_info_form = QtWidgets.QGridLayout()
self.layout_game_info_form.setContentsMargins(6, 6, 6, 6) self.layout_game_info_form.setContentsMargins(6, 6, 6, 6)
self.layout_game_info_form.setSpacing(12) self.layout_game_info_form.setSpacing(12)

View file

@ -14,19 +14,6 @@
<string>Game Info</string> <string>Game Info</string>
</property> </property>
<layout class="QHBoxLayout" name="layout_game_info"> <layout class="QHBoxLayout" name="layout_game_info">
<item alignment="Qt::AlignTop">
<widget class="QLabel" name="image">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item> <item>
<layout class="QGridLayout" name="layout_game_info_form"> <layout class="QGridLayout" name="layout_game_info_form">
<property name="leftMargin"> <property name="leftMargin">

View file

@ -1,11 +1,10 @@
import math
import os import os
import platform import platform
import shlex import shlex
import subprocess import subprocess
import sys import sys
from logging import getLogger from logging import getLogger
from typing import List, Tuple from typing import List
import qtawesome import qtawesome
import requests import requests
@ -306,35 +305,6 @@ def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link=
return False return False
def optimal_text_background(image: list) -> Tuple[int, int, int]:
"""
Finds an optimal background color for text on the image by calculating the
average color of the image and inverting it.
The image list is supposed to be a one-dimensional list of arbitrary length
containing RGB tuples, ranging from 0 to 255.
"""
# cursed, I know
average = map(lambda value: value // len(image), map(sum, zip(*image)))
inverted = map(lambda value: 255 - value, average)
return tuple(inverted)
def text_color_for_background(background: Tuple[int, int, int]) -> Tuple[int, int, int]:
"""
Calculates whether a black or white text color would fit better for the
given background, and returns that color. This is done by calculating the
luminance and simple comparing of bounds
"""
# see https://alienryderflex.com/hsp.html
(red, green, blue) = background
luminance = math.sqrt(0.299 * red ** 2 + 0.587 * green ** 2 + 0.114 * blue ** 2)
if luminance < 127:
return 255, 255, 255
else:
return 0, 0, 0
class WineResolverSignals(QObject): class WineResolverSignals(QObject):
result_ready = pyqtSignal(str) result_ready = pyqtSignal(str)

View file

@ -0,0 +1,144 @@
from enum import Enum
from typing import Tuple, Optional, Union
from PyQt5.QtCore import Qt, QRectF
from PyQt5.QtGui import (
QPaintEvent,
QPainter,
QPixmap,
QTransform,
QBrush,
QPalette,
QPainterPath,
QLinearGradient,
QColor,
)
from PyQt5.QtWidgets import QWidget
from rare.shared.image_manager import ImageSize
OverlayPath = Tuple[QPainterPath, Union[QColor, QLinearGradient]]
class ImageWidget(QWidget):
class Border(Enum):
Rounded = 0
Squared = 1
_pixmap: Optional[QPixmap] = None
_opacity: float = 1.0
_transform: QTransform
_smooth_transform: bool = False
_rounded_overlay: Optional[OverlayPath] = None
_squared_overlay: Optional[OverlayPath] = None
_image_size: Optional[ImageSize.Preset] = None
def __init__(self, parent=None) -> None:
super(ImageWidget, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
self.setContentsMargins(0, 0, 0, 0)
self.paint_image = self.paint_image_empty
self.paint_overlay = self.paint_overlay_rounded
def setOpacity(self, value: float) -> None:
self._opacity = value
self.update()
def setPixmap(self, pixmap: QPixmap) -> None:
if not pixmap.isNull():
self._pixmap = pixmap
self.paint_image = self.paint_image_cover
if not self._image_size:
self._transform = QTransform().scale(
1 / pixmap.devicePixelRatioF(),
1 / pixmap.devicePixelRatioF(),
)
else:
self._transform = QTransform().scale(
1 / pixmap.devicePixelRatioF() / self._image_size.divisor,
1 / pixmap.devicePixelRatioF() / self._image_size.divisor,
)
self.update()
def setFixedSize(self, a0: ImageSize.Preset) -> None:
self._squared_overlay = None
self._rounded_overlay = None
self._image_size = a0
self._smooth_transform = a0.smooth
super(ImageWidget, self).setFixedSize(a0.size)
def setBorder(self, border: Border):
if border == ImageWidget.Border.Rounded:
self.paint_overlay = self.paint_overlay_rounded
else:
self.paint_overlay = self.paint_overlay_squared
self.update()
def _generate_squared_overlay(self) -> OverlayPath:
if self._image_size is not None and self._squared_overlay is not None:
return self._squared_overlay
path = QPainterPath()
path.addRect(0, 0, self.width(), self.height())
border = 2
inner_path = QPainterPath()
inner_path.addRect(
QRectF(
border,
border,
self.width() - border * 2,
self.height() - border * 2,
)
)
gradient = QLinearGradient(0, 0, 0, self.height())
gradient.setColorAt(0.0, Qt.black)
gradient.setColorAt(1.0, Qt.transparent)
self._squared_overlay = path.subtracted(inner_path), gradient
return self._squared_overlay
def _generate_rounded_overlay(self) -> OverlayPath:
if self._image_size is not None and self._rounded_overlay is not None:
return self._rounded_overlay
# lk: the '-1' and '+1' are adjustments required for anti-aliasing
# lk: otherwise vertical lines would appear at the edges
path = QPainterPath()
path.addRect(-1, -1, self.width() + 2, self.height() + 2)
rounded_path = QPainterPath()
rounded_path.addRoundedRect(
QRectF(0, 0, self.width(), self.height()),
self.height() * 0.045,
self.height() * 0.045,
)
self._rounded_overlay = path.subtracted(rounded_path), QColor(0, 0, 0, 0)
return self._rounded_overlay
def paint_image_empty(self, painter: QPainter, a0: QPaintEvent) -> None:
# when pixmap object is not available yet, show a gray rectangle
painter.setOpacity(0.5 * self._opacity)
painter.fillRect(a0.rect(), Qt.darkGray)
def paint_image_cover(self, painter: QPainter, a0: QPaintEvent) -> None:
painter.setOpacity(self._opacity)
brush = QBrush(self._pixmap)
# downscale the image during painting to fit the pixelratio
brush.setTransform(self._transform)
painter.fillRect(a0.rect(), brush)
def paint_overlay_rounded(self, painter: QPainter, a0: QPaintEvent) -> None:
painter.setRenderHint(QPainter.Antialiasing, True)
painter.setOpacity(1.0)
painter.setCompositionMode(QPainter.CompositionMode_Source)
overlay, _ = self._generate_rounded_overlay()
painter.fillPath(overlay, self.palette().color(QPalette.Background))
def paint_overlay_squared(self, painter: QPainter, a0: QPaintEvent) -> None:
painter.setRenderHint(QPainter.Antialiasing, False)
painter.setOpacity(self._opacity)
painter.fillPath(*self._generate_squared_overlay())
def paintEvent(self, a0: QPaintEvent) -> None:
painter = QPainter(self)
# helps with better image quality
painter.setRenderHint(QPainter.SmoothPixmapTransform, self._smooth_transform)
self.paint_image(painter, a0)
self.paint_overlay(painter, a0)
painter.end()