Skip to content

Commit

Permalink
Store: Exploratory changes to the store page
Browse files Browse the repository at this point in the history
Important changes:
* Refactored QtRequests to accept parameters for `GET` operations
* Infer response data type from content-type header
* Support caching to disk, a manager with this set prefers the cache
* Support multiple handlers for a single request (unused, possibly pointeless)

* Subclass `ShopImageWidget` for all widgets used in the shop
* Request a resized image instead of the original one
* Fix the search and browse functions
  • Loading branch information
loathingKernel committed Apr 16, 2023
1 parent 3592ef3 commit 54915c5
Show file tree
Hide file tree
Showing 21 changed files with 576 additions and 524 deletions.
2 changes: 1 addition & 1 deletion rare/components/tabs/settings/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, parent=None):
self.update_lbl.setVisible(False)
self.open_browser.setVisible(False)

self.manager = QtRequestManager("json")
self.manager = QtRequestManager()
self.manager.get(
"https://api.github.com/repos/Dummerle/Rare/releases/latest",
self.update_available_finished,
Expand Down
8 changes: 4 additions & 4 deletions rare/components/tabs/store/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ def __init__(self, core: LegendaryCore, parent=None):
)

self.shop = ShopWidget(cache_dir(), self.core, self.api_core, parent=self)
self.shop_index = self.addTab(self.shop, self.tr("Games"))
self.shop_index = self.addTab(self.shop, self.tr("Store"))
self.shop.show_game.connect(self.show_game)
self.shop.show_info.connect(self.show_search)

self.search = SearchResults(self.api_core, parent=self)
self.search_index = self.addTab(self.search, self.tr("Search"))
self.search_index = self.addTab(self.search, self.tr("Search"), self.tr("Results"))
self.search.show_info.connect(self.show_game)
# self.search.back_button.clicked.connect(lambda: self.setCurrentIndex(self.shop_index))

Expand All @@ -40,11 +40,11 @@ def __init__(self, core: LegendaryCore, parent=None):
self.api_core,
parent=self
)
self.info_index = self.addTab(self.info, self.tr("Information"))
self.info_index = self.addTab(self.info, self.tr("Information"), self.tr("Information"))
# self.info.back_button.clicked.connect(lambda: self.setCurrentIndex(self.previous_index))

self.wishlist = Wishlist(self.api_core, parent=self)
self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"))
self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"), self.tr("Wishlist"))
self.wishlist.update_wishlist_signal.connect(self.update_wishlist)
self.wishlist.show_game_info.connect(self.show_game)

Expand Down
3 changes: 3 additions & 0 deletions rare/components/tabs/store/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ def __init__(self):
import rare.resources.stylesheets.RareStyle

app = QApplication(sys.argv)
app.setApplicationName("Rare")
app.setOrganizationName("Rare")

set_style_sheet("RareStyle")
window = StoreWindow()
window.setWindowTitle(f"{app.applicationName()} - Store")
window.resize(QSize(1280, 800))
window.show()
app.exec()
6 changes: 3 additions & 3 deletions rare/components/tabs/store/game_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, installed_titles: list, api_core, parent=None):
self.api_core = api_core
self.installed = installed_titles
self.ui.open_store_button.clicked.connect(self.button_clicked)
self.image = ShopImageWidget(self)
self.image = ShopImageWidget(api_core.cached_manager, self)
self.image.setFixedSize(ImageSize.Normal)
self.ui.left_layout.insertWidget(0, self.image, alignment=Qt.AlignTop)

Expand Down Expand Up @@ -139,7 +139,7 @@ def data_received(self, game):
"VaultClosed",
"ProductLogo",
]:
self.image.fetchPixmap(img["url"], self.id_str, self.title_str)
self.image.fetchPixmap(img["url"])
break
self.price.setText("")
self.discount_price.setText("")
Expand Down Expand Up @@ -207,7 +207,7 @@ def data_received(self, game):
img_url = self.game.image_urls.product_logo
else:
img_url = ""
self.image.fetchPixmap(img_url, self.game.id, self.game.title)
self.image.fetchPixmap(img_url)

# self.image_stack.setCurrentIndex(0)
try:
Expand Down
66 changes: 35 additions & 31 deletions rare/components/tabs/store/game_widgets.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import logging

from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QFont, QMouseEvent
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QPushButton

from rare.components.tabs.store.shop_models import ImageUrlModel
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.store.wishlist_widget import Ui_WishlistWidget
from rare.utils.extra_widgets import ImageLabel
from rare.utils.misc import icon
from rare.utils.qt_requests import QtRequestManager
from .image_widget import ShopImageWidget

logger = logging.getLogger("GameWidgets")
Expand All @@ -18,11 +16,10 @@
class GameWidget(ShopImageWidget):
show_info = pyqtSignal(dict)

def __init__(self, path, json_info=None, parent=None):
super(GameWidget, self).__init__(parent=parent)
def __init__(self, manager: QtRequestManager, json_info=None, parent=None):
super(GameWidget, self).__init__(manager, parent=parent)
self.setFixedSize(ImageSize.Wide)
self.ui.setupUi(self)
self.path = path
self.json_info = json_info
if json_info:
self.init_ui(json_info)
Expand All @@ -33,6 +30,13 @@ def init_ui(self, json_info):
return

self.ui.title_label.setText(json_info.get("title"))
for attr in json_info["customAttributes"]:
if attr["key"] == "developerName":
developer = attr["value"]
break
else:
developer = json_info["seller"]["name"]
self.ui.developer_label.setText(developer)
price = json_info["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
discount_price = json_info["price"]["totalPrice"]["fmtPrice"]["discountPrice"]
self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}')
Expand All @@ -51,7 +55,7 @@ def init_ui(self, json_info):
if img["type"] in ["DieselStoreFrontWide", "OfferImageWide", "VaultClosed", "ProductLogo",]:
if img["type"] == "VaultClosed" and json_info["title"] != "Mystery Game":
continue
self.fetchPixmap(img["url"], json_info["id"], json_info["title"])
self.fetchPixmap(img["url"])
break
else:
logger.info(", ".join([img["type"] for img in json_info["keyImages"]]))
Expand All @@ -62,52 +66,52 @@ def mousePressEvent(self, a0: QMouseEvent) -> None:
self.show_info.emit(self.json_info)


class WishlistWidget(QWidget, Ui_WishlistWidget):
class WishlistWidget(ShopImageWidget):
open_game = pyqtSignal(dict)
delete_from_wishlist = pyqtSignal(dict)

def __init__(self, game: dict):
super(WishlistWidget, self).__init__()
self.setupUi(self)
def __init__(self, manager: QtRequestManager, game: dict, parent=None):
super(WishlistWidget, self).__init__(manager, parent=parent)
self.setFixedSize(ImageSize.Wide)
self.ui.setupUi(self)
self.game = game
self.title_label.setText(game.get("title"))
for attr in game["customAttributes"]:
if attr["key"] == "developerName":
self.developer.setText(attr["value"])
developer = attr["value"]
break
else:
self.developer.setText(game["seller"]["name"])
developer = game["seller"]["name"]
original_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
discount_price = game["price"]["totalPrice"]["fmtPrice"]["discountPrice"]

self.price.setText(original_price if original_price != "0" else self.tr("Free"))
# if discount
self.ui.title_label.setText(game.get("title"))
self.ui.developer_label.setText(developer)
self.ui.price_label.setText(f'{original_price if original_price != "0" else self.tr("Free")}')
if original_price != discount_price:
self.discount = True
font = QFont()
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.price.setFont(font)
self.discount_price.setText(discount_price)
self.ui.price_label.setFont(font)
self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.discount = False
self.discount_price.setVisible(False)

self.image = ImageLabel()
self.layout().insertWidget(0, self.image)
self.ui.discount_label.setVisible(False)
image_model = ImageUrlModel.from_json(game["keyImages"])
url = image_model.front_wide
if not url:
url = image_model.offer_image_wide
self.image.update_image(url, game.get("title"), (240, 135))
self.fetchPixmap(url)

self.delete_button = QPushButton(self)
self.delete_button.setIcon(icon("mdi.delete", color="white"))
self.delete_button.clicked.connect(
lambda: self.delete_from_wishlist.emit(self.game)
)
self.layout().insertWidget(0, self.delete_button, alignment=Qt.AlignRight)

def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
def mousePressEvent(self, a0: QMouseEvent) -> None:
# left button
if e.button() == 1:
if a0.button() == Qt.LeftButton:
a0.accept()
self.open_game.emit(self.game)
# right
elif e.button() == 2:
elif a0.button() == Qt.RightButton:
pass # self.showMenu(e)
93 changes: 54 additions & 39 deletions rare/components/tabs/store/image_widget.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import Dict

from PyQt5.QtCore import QRect
from PyQt5.QtCore import QEvent, QObject
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
QPixmap,
QImage, QMovie,
QImage,
QMovie,
QShowEvent,
)
from PyQt5.QtWidgets import (
QWidget,
Expand All @@ -24,26 +24,51 @@ def __init__(self, autostart=False, parent=None):
super(WaitingSpinner, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
self.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.movie = QMovie(":/images/loader.gif")
self.movie = QMovie(":/images/loader.gif", parent=self)
self.setFixedSize(128, 128)
self.setMovie(self.movie)
if self.parent() is not None:
self.parent().installEventFilter(self)
if autostart:
self.movie.start()

def setGeometry(self, a0: QRect) -> None:
self.rect().moveCenter(self.parent().rect().center())
super(WaitingSpinner, self).setGeometry(self.rect())
def __center_on_parent(self):
rect = self.rect()
rect.moveCenter(self.parent().contentsRect().center())
self.setGeometry(rect)

def event(self, e: QEvent) -> bool:
if e.type() == QEvent.ParentAboutToChange:
if self.parent() is not None:
self.parent().removeEventFilter(self)
if e.type() == QEvent.ParentChange:
if self.parent() is not None:
self.parent().installEventFilter(self)
return super().event(e)

def showEvent(self, a0: QShowEvent) -> None:
self.__center_on_parent()

def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
if a0 is self.parent() and a1.type() == QEvent.Resize:
self.__center_on_parent()
return a0.event(a1)
return False

def start(self):
self.setVisible(True)
self.movie.start()

def stop(self):
self.setVisible(False)
self.movie.stop()


class IconWidget(object):
def __init__(self):
self.mini_widget: QWidget = None
self.title_label: QLabel = None
self.developer_label: QLabel = None
self.price_label: QLabel = None
self.discount_label: QLabel = None

Expand All @@ -57,19 +82,24 @@ def setupUi(self, widget: QWidget):
self.title_label = QLabel(parent=self.mini_widget)
self.title_label.setObjectName(f"{type(self).__name__}TitleLabel")
self.title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.title_label.setAlignment(Qt.AlignVCenter)
self.title_label.setAlignment(Qt.AlignTop)
self.title_label.setAutoFillBackground(False)
self.title_label.setWordWrap(True)

# information below title
self.developer_label = QLabel(parent=self.mini_widget)
self.developer_label.setObjectName(f"{type(self).__name__}TooltipLabel")
self.developer_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.developer_label.setAutoFillBackground(False)

self.price_label = QLabel(parent=self.mini_widget)
self.price_label.setObjectName(f"{type(self).__name__}TooltipLabel")
self.price_label.setAlignment(Qt.AlignRight)
self.price_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.price_label.setAutoFillBackground(False)

self.discount_label = QLabel(parent=self.mini_widget)
self.discount_label.setObjectName(f"{type(self).__name__}TooltipLabel")
self.discount_label.setAlignment(Qt.AlignRight)
self.discount_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.discount_label.setAutoFillBackground(False)

# Create layouts
Expand All @@ -88,7 +118,8 @@ def setupUi(self, widget: QWidget):

# Layout the widgets
# (from inner to outer)
row_layout.addWidget(self.price_label, stretch=2)
row_layout.addWidget(self.developer_label, stretch=2)
row_layout.addWidget(self.price_label)
row_layout.addWidget(self.discount_label)
mini_layout.addWidget(self.title_label)
mini_layout.addLayout(row_layout)
Expand All @@ -100,45 +131,29 @@ def setupUi(self, widget: QWidget):


class ShopImageWidget(ImageWidget):
__image_cache: Dict[str, Dict[str, QPixmap]] = {}

def __init__(self, parent=None):
def __init__(self, manager: QtRequestManager, parent=None):
super(ShopImageWidget, self).__init__(parent=parent)
self.ui = IconWidget()
self.spinner = WaitingSpinner(parent=self)
self.spinner.setVisible(False)
self.manager = QtRequestManager("bytes")
self.app_id = ""
self.orientation = ""
self.manager = manager

def fetchPixmap(self, url, app_id: str, title: str = ""):
def fetchPixmap(self, url):
self.setPixmap(QPixmap())
self.app_id = app_id
if self._image_size.size.width() > self._image_size.size.height():
self.orientation = "wide"
else:
self.orientation = "tall"

if ShopImageWidget.__image_cache.get(self.app_id, None) is not None:
if pixmap := ShopImageWidget.__image_cache[self.app_id].get(self.orientation, None):
self.setPixmap(pixmap)
return
self.spinner.setFixedSize(self._image_size.size)
self.spinner.setVisible(True)
self.spinner.start()
self.manager.get(
url, self.__on_image_ready, payload={
"resize": 1, "w": self._image_size.size.width(), "h": self._image_size.size.height()
}
)
self.manager.get(url, self.__on_image_ready, params={
"resize": 1,
"w": self._image_size.base.size.width(),
"h": self._image_size.base.size.height(),
})

def __on_image_ready(self, data):
cover = QImage()
cover.loadFromData(data)
cover = cover.scaled(self._image_size.size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
# cover = cover.scaled(self._image_size.size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
cover.setDevicePixelRatio(self._image_size.base.pixel_ratio)
cover = cover.convertToFormat(QImage.Format_ARGB32_Premultiplied)
cover = QPixmap(cover)
ShopImageWidget.__image_cache.update({self.app_id: {self.orientation: cover}})
super(ShopImageWidget, self).setPixmap(cover)
self.setPixmap(cover)
self.spinner.stop()
self.spinner.setVisible(False)
Loading

0 comments on commit 54915c5

Please sign in to comment.