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:
parent
8888cb3aee
commit
6335293eef
|
@ -87,7 +87,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
logger.warning("No Unreal engine in library found")
|
||||
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.addWidget(self.uninstalled_info_tabs)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QPixmap, QResizeEvent
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
|
||||
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_widget import Ui_GameDlcWidget
|
||||
from rare.utils.models import InstallOptionsModel
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
|
||||
class GameDlc(QWidget, Ui_GameDlc):
|
||||
|
@ -94,13 +94,15 @@ class GameDlcWidget(QFrame, Ui_GameDlcWidget):
|
|||
self.setupUi(self)
|
||||
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.version.setText(dlc.app_version())
|
||||
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:
|
||||
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.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):
|
||||
self.action_button.setDisabled(True)
|
||||
self.action_button.setText(self.tr("Uninstalling"))
|
||||
|
|
|
@ -40,6 +40,7 @@ from rare.utils.legendary_utils import VerifyWorker
|
|||
from rare.utils.models import InstallOptionsModel
|
||||
from rare.utils.steam_grades import SteamWorker
|
||||
from rare.utils.utils import get_size
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
logger = getLogger("GameInfo")
|
||||
|
||||
|
@ -60,6 +61,10 @@ class GameInfo(QWidget, Ui_GameInfo):
|
|||
self.image_manager = ImageManagerSingleton()
|
||||
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":
|
||||
self.lbl_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.title.setTitle(self.game.app_title)
|
||||
|
||||
pixmap = self.image_manager.get_pixmap(self.game.app_name)
|
||||
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.image.setPixmap(self.image_manager.get_pixmap(self.game.app_name, color=True))
|
||||
|
||||
self.app_name.setText(self.game.app_name)
|
||||
if self.igame:
|
||||
|
|
|
@ -17,16 +17,17 @@ from rare.utils.extra_widgets import SideTabWidget
|
|||
from rare.utils.json_formatter import QJsonModel
|
||||
from rare.utils.models import InstallOptionsModel
|
||||
from rare.utils.steam_grades import SteamWorker
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
|
||||
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)
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.signals = GlobalSignalsSingleton()
|
||||
self.args = ArgumentsSingleton()
|
||||
|
||||
self.info = UninstalledInfo(ue_default_name)
|
||||
self.info = UninstalledInfo()
|
||||
self.info.install_button.setDisabled(self.args.offline)
|
||||
self.addTab(self.info, self.tr("Information"))
|
||||
|
||||
|
@ -70,7 +71,7 @@ class GameMetadataView(QTreeView):
|
|||
class UninstalledInfo(QWidget, Ui_GameInfo):
|
||||
game: Game
|
||||
|
||||
def __init__(self, ue_default_name, parent=None):
|
||||
def __init__(self, parent=None):
|
||||
super(UninstalledInfo, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.core = LegendaryCoreSingleton()
|
||||
|
@ -78,6 +79,10 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
|
|||
self.api_results = ApiResultsSingleton()
|
||||
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)
|
||||
if platform.system() != "Windows":
|
||||
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.resize(self.game_actions_stack.minimumSize())
|
||||
self.lbl_platform.setText(self.tr("Platforms"))
|
||||
self.ue_default_name = ue_default_name
|
||||
|
||||
def install_game(self):
|
||||
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")
|
||||
self.platform.setText(", ".join(available_platforms))
|
||||
|
||||
pixmap = self.image_manager.get_pixmap(game.app_name, color=False)
|
||||
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.image.setPixmap(self.image_manager.get_pixmap(self.game.app_name, color=True))
|
||||
|
||||
self.app_name.setText(self.game.app_name)
|
||||
self.version.setText(self.game.app_version("Windows"))
|
||||
|
|
|
@ -4,12 +4,13 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray
|
||||
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.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.utils.utils import create_desktop_link
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
logger = getLogger("Game")
|
||||
|
||||
|
@ -63,11 +64,9 @@ class BaseInstalledWidget(QGroupBox):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
self.image = QLabel()
|
||||
self.image.setFixedSize(ImageSize.Display.size)
|
||||
self.image.setPixmap(
|
||||
pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)
|
||||
)
|
||||
self.image = ImageWidget(self)
|
||||
self.image.setFixedSize(ImageSize.Display)
|
||||
self.image.setPixmap(pixmap)
|
||||
self.game_running = False
|
||||
self.offline = self.args.offline
|
||||
self.update_available = False
|
||||
|
@ -144,7 +143,7 @@ class BaseInstalledWidget(QGroupBox):
|
|||
def reload_image(self):
|
||||
self.image_manager.download_image_blocking(self.game, force=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):
|
||||
if type_of_link == "desktop":
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from logging import getLogger
|
||||
|
||||
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 rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
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.core = core
|
||||
self.image = QLabel()
|
||||
self.image.setFixedSize(ImageSize.Display.size)
|
||||
self.image.setPixmap(pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation))
|
||||
self.image = ImageWidget(self)
|
||||
self.image.setFixedSize(ImageSize.Display)
|
||||
self.image.setPixmap(pixmap)
|
||||
self.installing = False
|
||||
self.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
|
@ -35,7 +36,7 @@ class BaseUninstalledWidget(QGroupBox):
|
|||
def reload_image(self):
|
||||
self.image_manager.download_image_blocking(self.game, force=True)
|
||||
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):
|
||||
self.show_uninstalled_info.emit(self.game)
|
||||
|
|
|
@ -1,41 +1,46 @@
|
|||
from PyQt5.QtCore import Qt, QRect
|
||||
from PyQt5.QtGui import QPaintEvent, QPainter, QPixmap, QPen, QFont, QColor
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox, QWidget
|
||||
from legendary.models.game import Game
|
||||
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.utils.utils import (
|
||||
optimal_text_background,
|
||||
text_color_for_background,
|
||||
)
|
||||
from rare.widgets.elide_label import ElideLabel
|
||||
from .library_widget import LibraryWidget
|
||||
|
||||
|
||||
class InstallingGameWidget(QWidget):
|
||||
class InstallingGameWidget(QGroupBox):
|
||||
game: Game = None
|
||||
|
||||
def __init__(self):
|
||||
super(InstallingGameWidget, self).__init__()
|
||||
layout = QVBoxLayout()
|
||||
self.setObjectName("game_widget_icon")
|
||||
self.setProperty("noBorder", 1)
|
||||
self.setLayout(QVBoxLayout())
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.image_manager = ImageManagerSingleton()
|
||||
|
||||
self.pixmap = QPixmap()
|
||||
self.image_widget = PaintWidget()
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.image_widget.setFixedSize(ImageSize.Display.size)
|
||||
self.layout().addWidget(self.image_widget)
|
||||
self.image = LibraryWidget(parent=self)
|
||||
self.image.setFixedSize(ImageSize.Display)
|
||||
layout.addWidget(self.image)
|
||||
|
||||
self.title_label = QLabel(f"<h4>Error</h4>")
|
||||
self.title_label.setAutoFillBackground(False)
|
||||
self.title_label.setWordWrap(True)
|
||||
self.title_label.setFixedWidth(175)
|
||||
miniwidget = QWidget(self)
|
||||
miniwidget.setFixedWidth(ImageSize.Display.size.width())
|
||||
minilayout = QHBoxLayout()
|
||||
self.setObjectName("game_widget")
|
||||
minilayout.addWidget(self.title_label)
|
||||
self.layout().addLayout(minilayout)
|
||||
minilayout.setContentsMargins(0, 0, 0, 0)
|
||||
minilayout.setSpacing(0)
|
||||
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):
|
||||
if not app_name:
|
||||
|
@ -43,73 +48,11 @@ class InstallingGameWidget(QWidget):
|
|||
return
|
||||
self.game = self.core.get_game(app_name)
|
||||
self.title_label.setText(f"<h4>{self.game.app_title}</h4>")
|
||||
|
||||
self.image_widget.set_game(self.game.app_name)
|
||||
self.image.hideProgress(True)
|
||||
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):
|
||||
self.image_widget.progress = 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()
|
||||
self.image.updateProgress(s)
|
||||
|
|
124
rare/components/tabs/games/game_widgets/library_widget.py
Normal file
124
rare/components/tabs/games/game_widgets/library_widget.py
Normal 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
|
|
@ -25,12 +25,6 @@ class Ui_GameDlcWidget(object):
|
|||
GameDlcWidget.setFrameShadow(QtWidgets.QFrame.Plain)
|
||||
self.dlc_layout = QtWidgets.QHBoxLayout(GameDlcWidget)
|
||||
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.setObjectName("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.setObjectName("action_button")
|
||||
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)
|
||||
QtCore.QMetaObject.connectSlotsByName(GameDlcWidget)
|
||||
|
|
|
@ -25,20 +25,7 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="dlc_layout" stretch="0,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>
|
||||
<layout class="QHBoxLayout" name="dlc_layout" stretch="0,1,0">
|
||||
<item>
|
||||
<widget class="QWidget" name="dlc_info" native="true">
|
||||
<layout class="QFormLayout" name="dlc_info_layout">
|
||||
|
|
|
@ -17,12 +17,6 @@ class Ui_GameInfo(object):
|
|||
GameInfo.resize(791, 583)
|
||||
self.layout_game_info = QtWidgets.QHBoxLayout(GameInfo)
|
||||
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.setContentsMargins(6, 6, 6, 6)
|
||||
self.layout_game_info_form.setSpacing(12)
|
||||
|
|
|
@ -14,19 +14,6 @@
|
|||
<string>Game Info</string>
|
||||
</property>
|
||||
<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>
|
||||
<layout class="QGridLayout" name="layout_game_info_form">
|
||||
<property name="leftMargin">
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import math
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from logging import getLogger
|
||||
from typing import List, Tuple
|
||||
from typing import List
|
||||
|
||||
import qtawesome
|
||||
import requests
|
||||
|
@ -306,35 +305,6 @@ def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link=
|
|||
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):
|
||||
result_ready = pyqtSignal(str)
|
||||
|
||||
|
|
144
rare/widgets/image_widget.py
Normal file
144
rare/widgets/image_widget.py
Normal 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()
|
Loading…
Reference in a new issue