f6396f488a
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
160 lines
5.7 KiB
Python
160 lines
5.7 KiB
Python
from PyQt5.QtCore import QEvent, QObject
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import (
|
|
QPixmap,
|
|
QImage,
|
|
QMovie,
|
|
QShowEvent,
|
|
)
|
|
from PyQt5.QtWidgets import (
|
|
QWidget,
|
|
QVBoxLayout,
|
|
QSpacerItem,
|
|
QSizePolicy,
|
|
QHBoxLayout,
|
|
QLabel,
|
|
)
|
|
|
|
from rare.utils.qt_requests import QtRequestManager
|
|
from rare.widgets.image_widget import ImageWidget
|
|
|
|
|
|
class WaitingSpinner(QLabel):
|
|
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", 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 __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
|
|
|
|
def setupUi(self, widget: QWidget):
|
|
# on-hover popup
|
|
self.mini_widget = QWidget(parent=widget)
|
|
self.mini_widget.setObjectName(f"{type(self).__name__}MiniWidget")
|
|
self.mini_widget.setFixedHeight(widget.height() // 4)
|
|
|
|
# game title
|
|
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.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 | 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 | Qt.AlignVCenter)
|
|
self.discount_label.setAutoFillBackground(False)
|
|
|
|
# Create layouts
|
|
# layout on top of the image, holds the status label, a spacer item and the mini widget
|
|
image_layout = QVBoxLayout()
|
|
image_layout.setContentsMargins(2, 2, 2, 2)
|
|
|
|
# layout for the mini widget, holds the top row and the info label
|
|
mini_layout = QVBoxLayout()
|
|
mini_layout.setSpacing(0)
|
|
|
|
# layout for the top row, holds the title and the launch button
|
|
row_layout = QHBoxLayout()
|
|
row_layout.setSpacing(6)
|
|
row_layout.setAlignment(Qt.AlignBottom)
|
|
|
|
# Layout the widgets
|
|
# (from inner to outer)
|
|
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)
|
|
self.mini_widget.setLayout(mini_layout)
|
|
|
|
image_layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
|
|
image_layout.addWidget(self.mini_widget)
|
|
widget.setLayout(image_layout)
|
|
|
|
|
|
class ShopImageWidget(ImageWidget):
|
|
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 = manager
|
|
|
|
def fetchPixmap(self, url):
|
|
self.setPixmap(QPixmap())
|
|
self.spinner.setFixedSize(self._image_size.size)
|
|
self.spinner.start()
|
|
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.setDevicePixelRatio(self._image_size.base.pixel_ratio)
|
|
cover = cover.convertToFormat(QImage.Format_ARGB32_Premultiplied)
|
|
cover = QPixmap(cover)
|
|
self.setPixmap(cover)
|
|
self.spinner.stop()
|