1
0
Fork 0
mirror of synced 2024-05-05 05:02:52 +12:00

Merge pull request #388 from loathingKernel/shop_refactor

Refactor the Store page
This commit is contained in:
Stelios Tsampas 2024-02-26 12:47:24 +02:00 committed by GitHub
commit a35e430539
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 4766 additions and 2533 deletions

View file

@ -8,7 +8,7 @@ from .downloads import DownloadsTab
from .games import GamesTab
from .settings import SettingsTab
from .settings.debug import DebugSettings
from .store import Shop
from .store import StoreTab
from .tab_widgets import MainTabBar, TabButtonWidget
@ -39,7 +39,7 @@ class MainTabWidget(QTabWidget):
self.setTabEnabled(self.downloads_index, not self.args.offline)
if not self.args.offline:
self.store_tab = Shop(self.core)
self.store_tab = StoreTab(self.core, parent=self)
self.store_index = self.addTab(self.store_tab, self.tr("Store (Beta)"))
self.setTabEnabled(self.store_index, not self.args.offline)

View file

@ -1,61 +1,42 @@
from PyQt5.QtGui import QShowEvent, QHideEvent
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
from legendary.core import LegendaryCore
from rare.shared.rare_core import RareCore
from rare.utils.paths import cache_dir
from .game_info import ShopGameInfo
from .search_results import SearchResults
from .shop_api_core import ShopApiCore
from .shop_widget import ShopWidget
from .wishlist import WishlistWidget, Wishlist
from rare.widgets.side_tab import SideTabWidget
from .api.models.response import CatalogOfferModel
from .landing import LandingWidget, LandingPage
from .search import SearchPage
from .store_api import StoreAPI
from .widgets.details import DetailsWidget
from .wishlist import WishlistPage
class Shop(QStackedWidget):
init = False
class StoreTab(SideTabWidget):
def __init__(self, core: LegendaryCore, parent=None):
super(StoreTab, self).__init__(parent=parent)
self.init = False
def __init__(self, core: LegendaryCore):
super(Shop, self).__init__()
self.core = core
self.rcore = RareCore.instance()
self.api_core = ShopApiCore(
# self.rcore = RareCore.instance()
self.api = StoreAPI(
self.core.egs.session.headers["Authorization"],
self.core.language_code,
self.core.country_code,
[] # [i.asset_infos["Windows"].namespace for i in self.rcore.game_list if bool(i.asset_infos)]
)
self.shop = ShopWidget(cache_dir(), self.core, self.api_core)
self.wishlist_widget = Wishlist(self.api_core)
self.landing = LandingPage(self.api, parent=self)
self.landing_index = self.addTab(self.landing, self.tr("Store"))
self.store_tabs = QTabWidget(parent=self)
self.store_tabs.addTab(self.shop, self.tr("Games"))
self.store_tabs.addTab(self.wishlist_widget, self.tr("Wishlist"))
self.search = SearchPage(self.api, parent=self)
self.search_index = self.addTab(self.search, self.tr("Search"))
self.addWidget(self.store_tabs)
self.search_results = SearchResults(self.api_core)
self.addWidget(self.search_results)
self.search_results.show_info.connect(self.show_game_info)
self.info = ShopGameInfo(
[i.asset_infos["Windows"].namespace for i in self.rcore.game_list if bool(i.asset_infos)],
self.api_core,
)
self.addWidget(self.info)
self.info.back_button.clicked.connect(lambda: self.setCurrentIndex(0))
self.search_results.back_button.clicked.connect(lambda: self.setCurrentIndex(0))
self.shop.show_info.connect(self.show_search_results)
self.wishlist_widget.show_game_info.connect(self.show_game_info)
self.shop.show_game.connect(self.show_game_info)
self.api_core.update_wishlist.connect(self.update_wishlist)
self.wishlist_widget.update_wishlist_signal.connect(self.update_wishlist)
self.wishlist = WishlistPage(self.api, parent=self)
self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"))
def showEvent(self, a0: QShowEvent) -> None:
if a0.spontaneous() or self.init:
return super().showEvent(a0)
self.shop.load()
self.wishlist_widget.update_wishlist()
self.init = True
return super().showEvent(a0)
@ -64,14 +45,3 @@ class Shop(QStackedWidget):
return super().hideEvent(a0)
# TODO: Implement store unloading
return super().hideEvent(a0)
def update_wishlist(self):
self.shop.update_wishlist()
def show_game_info(self, data):
self.info.update_game(data)
self.setCurrentIndex(2)
def show_search_results(self, text: str):
self.search_results.load_results(text)
self.setCurrentIndex(1)

View file

@ -0,0 +1,39 @@
import sys
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QDialog, QApplication, QVBoxLayout
from legendary.core import LegendaryCore
from . import StoreTab
class StoreWindow(QDialog):
def __init__(self):
super().__init__()
self.core = LegendaryCore()
self.core.login()
self.store_tab = StoreTab(self.core, self)
layout = QVBoxLayout(self)
layout.addWidget(self.store_tab)
self.store_tab.show()
if __name__ == "__main__":
import rare.resources.static_css
# import rare.resources.stylesheets.RareStyle
from rare.utils.misc import set_style_sheet
app = QApplication(sys.argv)
app.setApplicationName("Rare")
app.setOrganizationName("Rare")
set_style_sheet("")
set_style_sheet("RareStyle")
window = StoreWindow()
window.setWindowTitle(f"{app.applicationName()} - Store")
window.resize(QSize(1280, 800))
window.show()
app.exec()

View file

@ -0,0 +1,568 @@
FEED_QUERY = '''
query feedQuery(
$locale: String!
$countryCode: String
$offset: Int
$postsPerPage: Int
$category: String
) {
TransientStream {
myTransientFeed(countryCode: $countryCode, locale: $locale) {
id
activity {
... on LinkAccountActivity {
type
created_at
platforms
}
... on SuggestedFriendsActivity {
type
created_at
platform
suggestions {
epicId
epicDisplayName
platformFullName
platformAvatar
}
}
... on IncomingInvitesActivity {
type
created_at
invites {
epicId
epicDisplayName
}
}
... on RecentPlayersActivity {
type
created_at
players {
epicId
epicDisplayName
playedGameName
}
}
}
}
}
Blog {
dieselBlogPosts: getPosts(
locale: $locale
offset: $offset
postsPerPage: $postsPerPage
category: $category
) {
blogList {
_id
author
category
content
urlPattern
slug
sticky
title
date
image
shareImage
trendingImage
url
featured
link
externalLink
}
}
}
}
'''
REVIEWS_QUERY = '''
query productReviewsQuery($sku: String!) {
OpenCritic {
productReviews(sku: $sku) {
id
name
openCriticScore
reviewCount
percentRecommended
openCriticUrl
award
topReviews {
publishedDate
externalUrl
snippet
language
score
author
ScoreFormat {
id
description
}
OutletId
outletName
displayScore
}
}
}
}
'''
MEDIA_QUERY = '''
query fetchMediaRef($mediaRefId: String!) {
Media {
getMediaRef(mediaRefId: $mediaRefId) {
accountId
outputs {
duration
url
width
height
key
contentType
}
namespace
}
}
}
'''
ADDONS_QUERY = '''
query getAddonsByNamespace(
$categories: String!
$count: Int!
$country: String!
$locale: String!
$namespace: String!
$sortBy: String!
$sortDir: String!
) {
Catalog {
catalogOffers(
namespace: $namespace
locale: $locale
params: {
category: $categories
count: $count
country: $country
sortBy: $sortBy
sortDir: $sortDir
}
) {
elements {
countriesBlacklist
customAttributes {
key
value
}
description
developer
effectiveDate
id
isFeatured
keyImages {
type
url
}
lastModifiedDate
longDescription
namespace
offerType
productSlug
releaseDate
status
technicalDetails
title
urlSlug
}
}
}
}
'''
CATALOG_QUERY = '''
query catalogQuery(
$category: String
$count: Int
$country: String!
$keywords: String
$locale: String
$namespace: String!
$sortBy: String
$sortDir: String
$start: Int
$tag: String
) {
Catalog {
catalogOffers(
namespace: $namespace
locale: $locale
params: {
count: $count
country: $country
category: $category
keywords: $keywords
sortBy: $sortBy
sortDir: $sortDir
start: $start
tag: $tag
}
) {
elements {
isFeatured
collectionOfferIds
title
id
namespace
description
keyImages {
type
url
}
seller {
id
name
}
productSlug
urlSlug
items {
id
namespace
}
customAttributes {
key
value
}
categories {
path
}
price(country: $country) {
totalPrice {
discountPrice
originalPrice
voucherDiscount
discount
fmtPrice(locale: $locale) {
originalPrice
discountPrice
intermediatePrice
}
}
lineOffers {
appliedRules {
id
endDate
}
}
}
linkedOfferId
linkedOffer {
effectiveDate
customAttributes {
key
value
}
}
}
paging {
count
total
}
}
}
}
'''
CATALOG_TAGS_QUERY = '''
query catalogTags($namespace: String!) {
Catalog {
tags(namespace: $namespace, start: 0, count: 999) {
elements {
aliases
id
name
referenceCount
status
}
}
}
}
'''
PREREQUISITES_QUERY = '''
query fetchPrerequisites($offerParams: [OfferParams]) {
Launcher {
prerequisites(offerParams: $offerParams) {
namespace
offerId
missingPrerequisiteItems
satisfiesPrerequisites
}
}
}
'''
PROMOTIONS_QUERY = '''
query promotionsQuery(
$namespace: String!
$country: String!
$locale: String!
) {
Catalog {
catalogOffers(
namespace: $namespace
locale: $locale
params: {
category: "freegames"
country: $country
sortBy: "effectiveDate"
sortDir: "asc"
}
) {
elements {
title
description
id
namespace
categories {
path
}
linkedOfferNs
linkedOfferId
keyImages {
type
url
}
productSlug
promotions {
promotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
upcomingPromotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
}
}
}
}
}
'''
OFFERS_QUERY = '''
query catalogQuery(
$productNamespace: String!
$offerId: String!
$locale: String
$country: String!
$includeSubItems: Boolean!
) {
Catalog {
catalogOffer(namespace: $productNamespace, id: $offerId, locale: $locale) {
title
id
namespace
description
effectiveDate
expiryDate
isCodeRedemptionOnly
keyImages {
type
url
}
seller {
id
name
}
productSlug
urlSlug
url
tags {
id
}
items {
id
namespace
}
customAttributes {
key
value
}
categories {
path
}
price(country: $country) {
totalPrice {
discountPrice
originalPrice
voucherDiscount
discount
currencyCode
currencyInfo {
decimals
}
fmtPrice(locale: $locale) {
originalPrice
discountPrice
intermediatePrice
}
}
lineOffers {
appliedRules {
id
endDate
discountSetting {
discountType
}
}
}
}
}
offerSubItems(namespace: $productNamespace, id: $offerId)
@include(if: $includeSubItems) {
namespace
id
releaseInfo {
appId
platform
}
}
}
}
'''
SEARCH_STORE_QUERY = '''
query searchStoreQuery(
$allowCountries: String
$category: String
$count: Int
$country: String!
$keywords: String
$locale: String
$namespace: String
$itemNs: String
$sortBy: String
$sortDir: String
$start: Int
$tag: String
$releaseDate: String
$withPrice: Boolean = false
$withPromotions: Boolean = false
) {
Catalog {
searchStore(
allowCountries: $allowCountries
category: $category
count: $count
country: $country
keywords: $keywords
locale: $locale
namespace: $namespace
itemNs: $itemNs
sortBy: $sortBy
sortDir: $sortDir
releaseDate: $releaseDate
start: $start
tag: $tag
) {
elements {
title
id
namespace
description
effectiveDate
keyImages {
type
url
}
seller {
id
name
}
productSlug
urlSlug
url
tags {
id
}
items {
id
namespace
}
customAttributes {
key
value
}
categories {
path
}
price(country: $country) @include(if: $withPrice) {
totalPrice {
discountPrice
originalPrice
voucherDiscount
discount
currencyCode
currencyInfo {
decimals
}
fmtPrice(locale: $locale) {
originalPrice
discountPrice
intermediatePrice
}
}
lineOffers {
appliedRules {
id
endDate
discountSetting {
discountType
}
}
}
}
promotions(category: $category) @include(if: $withPromotions) {
promotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
upcomingPromotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
}
}
paging {
count
total
}
}
}
}
'''

View file

@ -0,0 +1,29 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QTreeView, QDialog, QVBoxLayout
from rare.utils.json_formatter import QJsonModel
class DebugView(QTreeView):
def __init__(self, data, parent=None):
super(DebugView, self).__init__(parent=parent)
self.setColumnWidth(0, 300)
self.setWordWrap(True)
self.model = QJsonModel(self)
self.setModel(self.model)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
try:
self.model.load(data)
except Exception as e:
pass
self.resizeColumnToContents(0)
class DebugDialog(QDialog):
def __init__(self, data, parent=None):
super().__init__(parent=parent)
self.resize(800, 600)
layout = QVBoxLayout(self)
view = DebugView(data, self)
layout.addWidget(view)

View file

@ -0,0 +1,15 @@
{
"name": "EGS GraphQL Schema",
"schemaPath": "schema.graphql",
"extensions": {
"endpoints": {
"Default GraphQL Endpoint": {
"url": "http://localhost:8080/graphql",
"headers": {
"user-agent": "JS GraphQL"
},
"introspect": false
}
}
}
}

View file

@ -0,0 +1,76 @@
scalar Date
type Currency {
decimals: Int
symbol: String
}
type FormattedPrice {
originalPrice: String
discountPrice: String
intermediatePrice: String
}
type TotalPrice {
discountPrice: Int
originalPrice: Int
voucherDiscount: Int
discount: Int
currencyCode: String
currencyInfo: Currency
fmtPrice(locale: String): FormattedPrice
}
type DiscountSetting {
discountType: String
}
type AppliedRules {
id: ID
endDate: Date
discountSetting: DiscountSetting
}
type LineOfferRes {
appliedRules: [AppliedRules]
}
type GetPriceRes {
totalPrice: TotalPrice
lineOffers: [LineOfferRes]
}
type Image {
type: String
url: String
alt: String
}
type StorePageMapping {
cmsSlug: String
offerId: ID
prePurchaseOfferId: ID
}
type PageSandboxModel {
pageSlug: String
pageType: String
productId: ID
sandboxId: ID
createdDate: Date
updatedDate: Date
deletedDate: Date
mappings: [StorePageMapping]
}
type CatalogNamespace {
parent: ID
displayName: String
store: String
mappings: [PageSandboxModel]
}
type CatalogItem {
id: ID
namespace: ID
}

View file

@ -0,0 +1,164 @@
import logging
from dataclasses import dataclass, field
from typing import List, Dict, Any, Type, Optional
logger = logging.getLogger("DieselModels")
# lk: Typing overloads for unimplemented types
DieselSocialLinks = Dict
@dataclass
class DieselSystemDetailItem:
_type: Optional[str] = None
minimum: Optional[str] = None
recommended: Optional[str] = None
title: Optional[str] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselSystemDetailItem"], src: Dict[str, Any]) -> "DieselSystemDetailItem":
d = src.copy()
tmp = cls(
_type=d.pop("_type", ""),
minimum=d.pop("minimum", ""),
recommended=d.pop("recommended", ""),
title=d.pop("title", ""),
)
tmp.unmapped = d
return tmp
@dataclass
class DieselSystemDetail:
_type: Optional[str] = None
details: Optional[List[DieselSystemDetailItem]] = None
systemType: Optional[str] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselSystemDetail"], src: Dict[str, Any]) -> "DieselSystemDetail":
d = src.copy()
_details = d.pop("details", [])
details = [] if _details else None
for item in _details:
detail = DieselSystemDetailItem.from_dict(item)
details.append(detail)
tmp = cls(
_type=d.pop("_type", ""),
details=details,
systemType=d.pop("systemType", ""),
)
tmp.unmapped = d
return tmp
@dataclass
class DieselSystemDetails:
_type: Optional[str] = None
languages: Optional[List[str]] = None
rating: Optional[Dict] = None
systems: Optional[List[DieselSystemDetail]] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselSystemDetails"], src: Dict[str, Any]) -> "DieselSystemDetails":
d = src.copy()
_systems = d.pop("systems", [])
systems = [] if _systems else None
for item in _systems:
system = DieselSystemDetail.from_dict(item)
systems.append(system)
tmp = cls(
_type=d.pop("_type", ""),
languages=d.pop("languages", []),
rating=d.pop("rating", {}),
systems=systems,
)
tmp.unmapped = d
return tmp
@dataclass
class DieselProductAbout:
_type: Optional[str] = None
desciption: Optional[str] = None
developerAttribution: Optional[str] = None
publisherAttribution: Optional[str] = None
shortDescription: Optional[str] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselProductAbout"], src: Dict[str, Any]) -> "DieselProductAbout":
d = src.copy()
tmp = cls(
_type=d.pop("_type", ""),
desciption=d.pop("description", ""),
developerAttribution=d.pop("developerAttribution", ""),
publisherAttribution=d.pop("publisherAttribution", ""),
shortDescription=d.pop("shortDescription", ""),
)
tmp.unmapped = d
return tmp
@dataclass
class DieselProductDetail:
_type: Optional[str] = None
about: Optional[DieselProductAbout] = None
requirements: Optional[DieselSystemDetails] = None
socialLinks: Optional[DieselSocialLinks] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselProductDetail"], src: Dict[str, Any]) -> "DieselProductDetail":
d = src.copy()
about = DieselProductAbout.from_dict(x) if (x := d.pop("about"), {}) else None
requirements = DieselSystemDetails.from_dict(x) if (x := d.pop("requirements", {})) else None
tmp = cls(
_type=d.pop("_type", ""),
about=about,
requirements=requirements,
socialLinks=d.pop("socialLinks", {}),
)
tmp.unmapped = d
return tmp
@dataclass
class DieselProduct:
_id: Optional[str] = None
_images_: Optional[List[str]] = None
_locale: Optional[str] = None
_slug: Optional[str] = None
_title: Optional[str] = None
_urlPattern: Optional[str] = None
namespace: Optional[str] = None
pages: Optional[List["DieselProduct"]] = None
data: Optional[DieselProductDetail] = None
productName: Optional[str] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DieselProduct"], src: Dict[str, Any]) -> "DieselProduct":
d = src.copy()
_pages = d.pop("pages", [])
pages = [] if _pages else None
for item in _pages:
page = DieselProduct.from_dict(item)
pages.append(page)
data = DieselProductDetail.from_dict(x) if (x := d.pop("data", {})) else None
tmp = cls(
_id=d.pop("_id", ""),
_images_=d.pop("_images_", []),
_locale=d.pop("_locale", ""),
_slug=d.pop("_slug", ""),
_title=d.pop("_title", ""),
_urlPattern=d.pop("_urlPattern", ""),
namespace=d.pop("namespace", ""),
pages=pages,
data=data,
productName=d.pop("productName", ""),
)
tmp.unmapped = d
return tmp

View file

@ -0,0 +1,80 @@
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import List
@dataclass
class SearchDateRange:
start_date: datetime = datetime(year=1990, month=1, day=1, tzinfo=timezone.utc)
end_date: datetime = datetime.utcnow().replace(tzinfo=timezone.utc)
def __str__(self):
def fmt_date(date: datetime) -> str:
# lk: The formatting accepted by the GraphQL API is either '%Y-%m-%dT%H:%M:%S.000Z' or '%Y-%m-%d'
return datetime.strftime(date, '%Y-%m-%dT%H:%M:%S.000Z')
return f"[{fmt_date(self.start_date)},{fmt_date(self.end_date)}]"
@dataclass
class SearchStoreQuery:
country: str = "US"
category: str = "games/edition/base|bundles/games|editors|software/edition/base"
count: int = 30
keywords: str = ""
language: str = "en"
namespace: str = ""
with_mapping: bool = True
item_ns: str = ""
sort_by: str = "releaseDate"
sort_dir: str = "DESC"
start: int = 0
tag: List[str] = ""
release_date: SearchDateRange = field(default_factory=SearchDateRange)
with_price: bool = True
with_promotions: bool = True
price_range: str = ""
free_game: bool = None
on_sale: bool = None
effective_date: SearchDateRange = field(default_factory=SearchDateRange)
def __post_init__(self):
self.locale = f"{self.language}-{self.country}"
def to_dict(self):
payload = {
"allowCountries": self.country,
"category": self.category,
"count": self.count,
"country": self.country,
"keywords": self.keywords,
"locale": self.locale,
"namespace": self.namespace,
"withMapping": self.with_mapping,
"itemNs": self.item_ns,
"sortBy": self.sort_by,
"sortDir": self.sort_dir,
"start": self.start,
"tag": self.tag,
"releaseDate": str(self.release_date),
"withPrice": self.with_price,
"withPromotions": self.with_promotions,
"priceRange": self.price_range,
"freeGame": self.free_game,
"onSale": self.on_sale,
"effectiveDate": str(self.effective_date),
}
# payload.pop("withPromotions")
payload.pop("onSale")
if self.price_range == "free":
payload["freeGame"] = True
payload.pop("priceRange")
elif self.price_range.startswith("<price>"):
payload["priceRange"] = self.price_range.replace("<price>", "")
if self.on_sale:
payload["onSale"] = True
if self.price_range:
payload["effectiveDate"] = self.effective_date
else:
payload.pop("priceRange")
return payload

View file

@ -0,0 +1,480 @@
import logging
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Any, Type, Optional, Tuple
from .utils import parse_date
logger = logging.getLogger("StoreApiModels")
# lk: Typing overloads for unimplemented types
DieselSocialLinks = Dict
CatalogNamespaceModel = Dict
CategoryModel = Dict
CustomAttributeModel = Dict
ItemModel = Dict
SellerModel = Dict
PageSandboxModel = Dict
TagModel = Dict
@dataclass
class ImageUrlModel:
type: Optional[str] = None
url: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
tmp: Dict[str, Any] = {}
tmp.update({})
if self.type is not None:
tmp["type"] = self.type
if self.url is not None:
tmp["url"] = self.url
return tmp
@classmethod
def from_dict(cls: Type["ImageUrlModel"], src: Dict[str, Any]) -> "ImageUrlModel":
d = src.copy()
type = d.pop("type", None)
url = d.pop("url", None)
tmp = cls(type=type, url=url)
return tmp
@dataclass
class KeyImagesModel:
key_images: Optional[List[ImageUrlModel]] = None
tall_types = ("DieselStoreFrontTall", "OfferImageTall", "Thumbnail", "ProductLogo", "DieselGameBoxLogo")
wide_types = ("DieselStoreFrontWide", "OfferImageWide", "VaultClosed", "ProductLogo")
def __getitem__(self, item):
return self.key_images[item]
def __bool__(self):
return bool(self.key_images)
def to_list(self) -> List[Dict[str, Any]]:
items: Optional[List[Dict[str, Any]]] = None
if self.key_images is not None:
items = []
for image_url in self.key_images:
item = image_url.to_dict()
items.append(item)
return items
@classmethod
def from_list(cls: Type["KeyImagesModel"], src: List[Dict]):
d = src.copy()
key_images = []
for item in d:
image_url = ImageUrlModel.from_dict(item)
key_images.append(image_url)
tmp = cls(key_images)
return tmp
def available_tall(self) -> List[ImageUrlModel]:
tall_images = filter(lambda img: img.type in KeyImagesModel.tall_types, self.key_images)
tall_images = sorted(tall_images, key=lambda x: KeyImagesModel.tall_types.index(x.type))
return tall_images
def available_wide(self) -> List[ImageUrlModel]:
wide_images = filter(lambda img: img.type in KeyImagesModel.wide_types, self.key_images)
wide_images = sorted(wide_images, key=lambda x: KeyImagesModel.wide_types.index(x.type))
return wide_images
def for_dimensions(self, w: int, h: int) -> ImageUrlModel:
try:
if w > h:
model = self.available_wide()[0]
else:
model = self.available_tall()[0]
_ = model.url
except Exception as e:
logger.error(e)
logger.error(self.to_list())
else:
return model
CurrencyModel = Dict
FormattedPriceModel = Dict
LineOffersModel = Dict
@dataclass
class TotalPriceModel:
discountPrice: Optional[int] = None
originalPrice: Optional[int] = None
voucherDiscount: Optional[int] = None
discount: Optional[int] = None
currencyCode: Optional[str] = None
currencyInfo: Optional[CurrencyModel] = None
fmtPrice: Optional[FormattedPriceModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["TotalPriceModel"], src: Dict[str, Any]) -> "TotalPriceModel":
d = src.copy()
tmp = cls(
discountPrice=d.pop("discountPrice", None),
originalPrice=d.pop("originalPrice", None),
voucherDiscount=d.pop("voucherDiscount", None),
discount=d.pop("discount", None),
currencyCode=d.pop("currencyCode", None),
currencyInfo=d.pop("currrencyInfo", {}),
fmtPrice=d.pop("fmtPrice", {}),
)
tmp.unmapped = d
return tmp
@dataclass
class GetPriceResModel:
totalPrice: Optional[TotalPriceModel] = None
lineOffers: Optional[LineOffersModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["GetPriceResModel"], src: Dict[str, Any]) -> "GetPriceResModel":
d = src.copy()
total_price = TotalPriceModel.from_dict(x) if (x := d.pop("totalPrice", {})) else None
tmp = cls(totalPrice=total_price, lineOffers=d.pop("lineOffers", {}))
tmp.unmapped = d
return tmp
DiscountSettingModel = Dict
@dataclass
class PromotionalOfferModel:
startDate: Optional[datetime] = None
endDate: Optional[datetime] = None
discountSetting: Optional[DiscountSettingModel] = None
@classmethod
def from_dict(cls: Type["PromotionalOfferModel"], src: Dict[str, Any]) -> "PromotionalOfferModel":
d = src.copy()
start_date = parse_date(x) if (x := d.pop("startDate", "")) else None
end_date = parse_date(x) if (x := d.pop("endDate", "")) else None
tmp = cls(startDate=start_date, endDate=end_date, discountSetting=d.pop("discountSetting", {}))
tmp.unmapped = d
return tmp
@dataclass
class PromotionalOffersModel:
promotionalOffers: Optional[Tuple[PromotionalOfferModel]] = None
@classmethod
def from_list(cls: Type["PromotionalOffersModel"], src: Dict[str, List]) -> "PromotionalOffersModel":
d = src.copy()
promotional_offers = (
tuple([PromotionalOfferModel.from_dict(y) for y in x]) if (x := d.pop("promotionalOffers", [])) else None
)
tmp = cls(promotionalOffers=promotional_offers)
tmp.unmapped = d
return tmp
@dataclass
class PromotionsModel:
promotionalOffers: Optional[Tuple[PromotionalOffersModel]] = None
upcomingPromotionalOffers: Optional[Tuple[PromotionalOffersModel]] = None
@classmethod
def from_dict(cls: Type["PromotionsModel"], src: Dict[str, Any]) -> "PromotionsModel":
d = src.copy()
promotional_offers = (
tuple([PromotionalOffersModel.from_list(y) for y in x]) if (x := d.pop("promotionalOffers", [])) else None
)
upcoming_promotional_offers = (
tuple([PromotionalOffersModel.from_list(y) for y in x])
if (x := d.pop("upcomingPromotionalOffers", []))
else None
)
tmp = cls(promotionalOffers=promotional_offers, upcomingPromotionalOffers=upcoming_promotional_offers)
tmp.unmapped = d
return tmp
@dataclass
class CatalogOfferModel:
catalogNs: Optional[CatalogNamespaceModel] = None
categories: Optional[List[CategoryModel]] = None
customAttributes: Optional[List[CustomAttributeModel]] = None
description: Optional[str] = None
effectiveDate: Optional[datetime] = None
expiryDate: Optional[datetime] = None
id: Optional[str] = None
isCodeRedemptionOnly: Optional[bool] = None
items: Optional[List[ItemModel]] = None
keyImages: Optional[KeyImagesModel] = None
namespace: Optional[str] = None
offerMappings: Optional[List[PageSandboxModel]] = None
offerType: Optional[str] = None
price: Optional[GetPriceResModel] = None
productSlug: Optional[str] = None
promotions: Optional[PromotionsModel] = None
seller: Optional[SellerModel] = None
status: Optional[str] = None
tags: Optional[List[TagModel]] = None
title: Optional[str] = None
url: Optional[str] = None
urlSlug: Optional[str] = None
viewableDate: Optional[datetime] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["CatalogOfferModel"], src: Dict[str, Any]) -> "CatalogOfferModel":
d = src.copy()
effective_date = parse_date(x) if (x := d.pop("effectiveDate", "")) else None
expiry_date = parse_date(x) if (x := d.pop("expiryDate", "")) else None
key_images = KeyImagesModel.from_list(d.pop("keyImages", []))
price = GetPriceResModel.from_dict(x) if (x := d.pop("price", {})) else None
promotions = PromotionsModel.from_dict(x) if (x := d.pop("promotions", {})) else None
viewable_date = parse_date(x) if (x := d.pop("viewableDate", "")) else None
tmp = cls(
catalogNs=d.pop("catalogNs", {}),
categories=d.pop("categories", []),
customAttributes=d.pop("customAttributes", []),
description=d.pop("description", ""),
effectiveDate=effective_date,
expiryDate=expiry_date,
id=d.pop("id", ""),
isCodeRedemptionOnly=d.pop("isCodeRedemptionOnly", None),
items=d.pop("items", []),
keyImages=key_images,
namespace=d.pop("namespace", ""),
offerMappings=d.pop("offerMappings", []),
offerType=d.pop("offerType", ""),
price=price,
productSlug=d.pop("productSlug", ""),
promotions=promotions,
seller=d.pop("seller", {}),
status=d.pop("status", ""),
tags=d.pop("tags", []),
title=d.pop("title", ""),
url=d.pop("url", ""),
urlSlug=d.pop("urlSlug", ""),
viewableDate=viewable_date,
)
tmp.unmapped = d
return tmp
@dataclass
class WishlistItemModel:
created: Optional[datetime] = None
id: Optional[str] = None
namespace: Optional[str] = None
isFirstTime: Optional[bool] = None
offerId: Optional[str] = None
order: Optional[Any] = None
updated: Optional[datetime] = None
offer: Optional[CatalogOfferModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["WishlistItemModel"], src: Dict[str, Any]) -> "WishlistItemModel":
d = src.copy()
created = parse_date(x) if (x := d.pop("created", "")) else None
offer = CatalogOfferModel.from_dict(x) if (x := d.pop("offer", {})) else None
updated = parse_date(x) if (x := d.pop("updated", "")) else None
tmp = cls(
created=created,
id=d.pop("id", ""),
namespace=d.pop("namespace", ""),
isFirstTime=d.pop("isFirstTime", None),
offerId=d.pop("offerId", ""),
order=d.pop("order", ""),
updated=updated,
offer=offer,
)
tmp.unmapped = d
return tmp
@dataclass
class PagingModel:
count: Optional[int] = None
total: Optional[int] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["PagingModel"], src: Dict[str, Any]) -> "PagingModel":
d = src.copy()
count = d.pop("count", None)
total = d.pop("total", None)
tmp = cls(count=count, total=total)
tmp.unmapped = d
return tmp
@dataclass
class SearchStoreModel:
elements: Optional[List[CatalogOfferModel]] = None
paging: Optional[PagingModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["SearchStoreModel"], src: Dict[str, Any]) -> "SearchStoreModel":
d = src.copy()
_elements = d.pop("elements", [])
elements = [] if _elements else None
for item in _elements:
elem = CatalogOfferModel.from_dict(item)
elements.append(elem)
paging = PagingModel.from_dict(x) if (x := d.pop("paging", {})) else None
tmp = cls(elements=elements, paging=paging)
tmp.unmapped = d
return tmp
@dataclass
class CatalogModel:
searchStore: Optional[SearchStoreModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["CatalogModel"], src: Dict[str, Any]) -> "CatalogModel":
d = src.copy()
search_store = SearchStoreModel.from_dict(x) if (x := d.pop("searchStore", {})) else None
tmp = cls(searchStore=search_store)
tmp.unmapped = d
return tmp
@dataclass
class WishlistItemsModel:
elements: Optional[List[WishlistItemModel]] = None
paging: Optional[PagingModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["WishlistItemsModel"], src: Dict[str, Any]) -> "WishlistItemsModel":
d = src.copy()
_elements = d.pop("elements", [])
elements = [] if _elements else None
for item in _elements:
elem = WishlistItemModel.from_dict(item)
elements.append(elem)
paging = PagingModel.from_dict(x) if (x := d.pop("paging", {})) else None
tmp = cls(elements=elements, paging=paging)
tmp.unmapped = d
return tmp
@dataclass
class RemoveFromWishlistModel:
success: Optional[bool] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["RemoveFromWishlistModel"], src: Dict[str, Any]) -> "RemoveFromWishlistModel":
d = src.copy()
tmp = cls(success=d.pop("success", None))
tmp.unmapped = d
return tmp
@dataclass
class AddToWishlistModel:
wishlistItem: Optional[WishlistItemModel] = None
success: Optional[bool] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["AddToWishlistModel"], src: Dict[str, Any]) -> "AddToWishlistModel":
d = src.copy()
wishlist_item = WishlistItemModel.from_dict(x) if (x := d.pop("wishlistItem", {})) else None
tmp = cls(wishlistItem=wishlist_item, success=d.pop("success", None))
tmp.unmapped = d
return tmp
@dataclass
class WishlistModel:
wishlistItems: Optional[WishlistItemsModel] = None
removeFromWishlist: Optional[RemoveFromWishlistModel] = None
addToWishlist: Optional[AddToWishlistModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["WishlistModel"], src: Dict[str, Any]) -> "WishlistModel":
d = src.copy()
wishlist_items = WishlistItemsModel.from_dict(x) if (x := d.pop("wishlistItems", {})) else None
remove_from_wishlist = RemoveFromWishlistModel.from_dict(x) if (x := d.pop("removeFromWishlist", {})) else None
add_to_wishlist = AddToWishlistModel.from_dict(x) if (x := d.pop("addToWishlist", {})) else None
tmp = cls(
wishlistItems=wishlist_items, removeFromWishlist=remove_from_wishlist, addToWishlist=add_to_wishlist
)
tmp.unmapped = d
return tmp
ProductModel = Dict
@dataclass
class DataModel:
product: Optional[ProductModel] = None
catalog: Optional[CatalogModel] = None
wishlist: Optional[WishlistModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["DataModel"], src: Dict[str, Any]) -> "DataModel":
d = src.copy()
catalog = CatalogModel.from_dict(x) if (x := d.pop("Catalog", {})) else None
wishlist = WishlistModel.from_dict(x) if (x := d.pop("Wishlist", {})) else None
tmp = cls(product=d.pop("Product", {}), catalog=catalog, wishlist=wishlist)
tmp.unmapped = d
return tmp
@dataclass
class ErrorModel:
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["ErrorModel"], src: Dict[str, Any]) -> "ErrorModel":
d = src.copy()
tmp = cls()
tmp.unmapped = d
return tmp
@dataclass
class ExtensionsModel:
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["ExtensionsModel"], src: Dict[str, Any]) -> "ExtensionsModel":
d = src.copy()
tmp = cls()
tmp.unmapped = d
return tmp
@dataclass
class ResponseModel:
data: Optional[DataModel] = None
errors: Optional[List[ErrorModel]] = None
extensions: Optional[ExtensionsModel] = None
unmapped: Dict[str, Any] = field(default_factory=dict)
@classmethod
def from_dict(cls: Type["ResponseModel"], src: Dict[str, Any]) -> "ResponseModel":
d = src.copy()
data = DataModel.from_dict(x) if (x := d.pop("data", {})) else None
_errors = d.pop("errors", [])
errors = [] if _errors else None
for item in _errors:
error = ErrorModel.from_dict(item)
errors.append(error)
extensions = ExtensionsModel.from_dict(x) if (x := d.pop("extensions", {})) else None
tmp = cls(data=data, errors=errors, extensions=extensions)
tmp.unmapped = d
return tmp

View file

@ -0,0 +1,5 @@
from datetime import datetime, timezone
def parse_date(date: str):
return datetime.fromisoformat(date[:-1]).replace(tzinfo=timezone.utc)

View file

@ -44,74 +44,411 @@ class Constants(QObject):
]
game_query = (
"query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, "
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, "
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean "
"= false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, "
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n "
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n "
"locale: $locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n "
"sortDir: $sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n "
"priceRange: $priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: "
"$effectiveDate\n ) {\n elements {\n title\n id\n namespace\n "
"description\n effectiveDate\n keyImages {\n type\n url\n }\n "
" currentPrice\n seller {\n id\n name\n }\n productSlug\n "
" urlSlug\n url\n tags {\n id\n }\n items {\n id\n "
" namespace\n }\n customAttributes {\n key\n value\n }\n "
"categories {\n path\n }\n catalogNs @include(if: $withMapping) {\n "
'mappings(pageType: "productHome") {\n pageSlug\n pageType\n }\n '
"}\n offerMappings @include(if: $withMapping) {\n pageSlug\n pageType\n "
"}\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n "
"discountPrice\n originalPrice\n voucherDiscount\n discount\n "
" currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice("
"locale: $locale) {\n originalPrice\n discountPrice\n "
"intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n "
" id\n endDate\n discountSetting {\n discountType\n "
" }\n }\n }\n }\n promotions(category: $category) @include(if: "
"$withPromotions) {\n promotionalOffers {\n promotionalOffers {\n "
"startDate\n endDate\n discountSetting {\n discountType\n "
" discountPercentage\n }\n }\n }\n "
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n "
"endDate\n discountSetting {\n discountType\n "
"discountPercentage\n }\n }\n }\n }\n }\n paging {\n "
" count\n total\n }\n }\n }\n}\n "
)
__Image = '''
type
url
alt
'''
search_query = (
"query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, "
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, "
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean = "
"false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, "
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n "
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n locale: "
"$locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n sortDir: "
"$sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n priceRange: "
"$priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: $effectiveDate\n ) {"
"\n elements {\n title\n id\n namespace\n description\n "
"effectiveDate\n keyImages {\n type\n url\n }\n currentPrice\n "
"seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n "
" tags {\n id\n }\n items {\n id\n namespace\n }\n "
"customAttributes {\n key\n value\n }\n categories {\n path\n "
'}\n catalogNs @include(if: $withMapping) {\n mappings(pageType: "productHome") {\n '
" pageSlug\n pageType\n }\n }\n offerMappings @include(if: $withMapping) "
"{\n pageSlug\n pageType\n }\n price(country: $country) @include(if: "
"$withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n "
"voucherDiscount\n discount\n currencyCode\n currencyInfo {\n "
"decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n "
"discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n "
" appliedRules {\n id\n endDate\n discountSetting {\n "
"discountType\n }\n }\n }\n }\n promotions(category: "
"$category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n "
" startDate\n endDate\n discountSetting {\n "
"discountType\n discountPercentage\n }\n }\n }\n "
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n "
"endDate\n discountSetting {\n discountType\n discountPercentage\n "
" }\n }\n }\n }\n }\n paging {\n count\n "
"total\n }\n }\n }\n}\n "
)
__StorePageMapping = '''
cmsSlug
offerId
prePurchaseOfferId
'''
wishlist_query = '\n query wishlistQuery($country:String!, $locale:String) {\n Wishlist {\n wishlistItems {\n elements {\n id\n order\n created\n offerId\n updated\n namespace\n \n offer {\n productSlug\n urlSlug\n title\n id\n namespace\n offerType\n expiryDate\n status\n isCodeRedemptionOnly\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n catalogNs {\n mappings(pageType: "productHome") {\n pageSlug\n pageType\n }\n }\n offerMappings {\n pageSlug\n pageType\n }\n categories {\n path\n }\n price(country: $country) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n currencyCode\n currencyInfo {\n decimals\n symbol\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n }\n }\n }\n }\n\n }\n }\n }\n }\n'
add_to_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"
remove_from_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"
coupon_query = "\n query getCoupons($currencyCountry: String!, $identityId: String!, $locale: String) {\n CodeRedemption {\n coupons(currencyCountry: $currencyCountry, identityId: $identityId, includeSalesEventInfo: true) {\n code\n codeStatus\n codeType\n consumptionMetadata {\n amountDisplay {\n amount\n currency\n placement\n symbol\n }\n minSalesPriceDisplay {\n amount\n currency\n placement\n symbol\n }\n }\n endDate\n namespace\n salesEvent(locale: $locale) {\n eventName\n eventSlug\n voucherImages {\n type\n url\n }\n voucherLink\n }\n startDate\n }\n }\n }\n"
__PageSandboxModel = '''
pageSlug
pageType
productId
sandboxId
createdDate
updatedDate
deletedDate
mappings {
%s
}
''' % (__StorePageMapping)
__CatalogNamespace = '''
parent
displayName
store
home: mappings(pageType: "productHome") {
%s
}
addons: mappings(pageType: "addon--cms-hybrid") {
%s
}
offers: mappings(pageType: "offer") {
%s
}
''' % (__PageSandboxModel, __PageSandboxModel, __PageSandboxModel)
__CatalogItem = '''
id
namespace
'''
__GetPriceRes = '''
totalPrice {
discountPrice
originalPrice
voucherDiscount
discount
currencyCode
currencyInfo {
decimals
symbol
}
fmtPrice(locale: $locale) {
originalPrice
discountPrice
intermediatePrice
}
}
lineOffers {
appliedRules {
id
endDate
discountSetting {
discountType
}
}
}
'''
__Promotions = '''
promotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
upcomingPromotionalOffers {
promotionalOffers {
startDate
endDate
discountSetting {
discountType
discountPercentage
}
}
}
'''
__CatalogOffer = '''
title
id
namespace
offerType
expiryDate
status
isCodeRedemptionOnly
description
effectiveDate
keyImages {
%(image)s
}
currentPrice
seller {
id
name
}
productSlug
urlSlug
url
tags {
id
name
groupName
}
items {
%(catalog_item)s
}
customAttributes {
key
value
}
categories {
path
}
catalogNs @include(if: $withMapping) {
%(catalog_namespace)s
}
offerMappings @include(if: $withMapping) {
%(page_sandbox_model)s
}
price(country: $country) @include(if: $withPrice) {
%(get_price_res)s
}
promotions(category: $category) @include(if: $withPromotions) {
%(promotions)s
}
''' % {
"image": __Image,
"catalog_item": __CatalogItem,
"catalog_namespace": __CatalogNamespace,
"page_sandbox_model": __PageSandboxModel,
"get_price_res": __GetPriceRes,
"promotions": __Promotions,
}
__Pagination = '''
count
total
'''
SEARCH_STORE_QUERY = '''
query searchStoreQuery(
$allowCountries: String
$category: String
$count: Int
$country: String!
$keywords: String
$locale: String
$namespace: String
$withMapping: Boolean = false
$itemNs: String
$sortBy: String
$sortDir: String
$start: Int
$tag: String
$releaseDate: String
$withPrice: Boolean = false
$withPromotions: Boolean = false
$priceRange: String
$freeGame: Boolean
$onSale: Boolean
$effectiveDate: String
) {
Catalog {
searchStore(
allowCountries: $allowCountries
category: $category
count: $count
country: $country
keywords: $keywords
locale: $locale
namespace: $namespace
itemNs: $itemNs
sortBy: $sortBy
sortDir: $sortDir
releaseDate: $releaseDate
start: $start
tag: $tag
priceRange: $priceRange
freeGame: $freeGame
onSale: $onSale
effectiveDate: $effectiveDate
) {
elements {
%s
}
paging {
%s
}
}
}
}
''' % (__CatalogOffer, __Pagination)
__WISHLIST_ITEM = '''
id
order
created
offerId
updated
namespace
isFirstTime
offer(locale: $locale) {
%s
}
''' % __CatalogOffer
WISHLIST_QUERY = '''
query wishlistQuery(
$country: String!
$locale: String
$category: String
$withMapping: Boolean = false
$withPrice: Boolean = false
$withPromotions: Boolean = false
) {
Wishlist {
wishlistItems {
elements {
%s
}
}
}
}
''' % __WISHLIST_ITEM
WISHLIST_ADD_QUERY = '''
mutation addWishlistMutation(
$namespace: String!
$offerId: String!
$country: String!
$locale: String
$category: String
$withMapping: Boolean = false
$withPrice: Boolean = false
$withPromotions: Boolean = false
) {
Wishlist {
addToWishlist(
namespace: $namespace
offerId: $offerId
) {
wishlistItem {
%s
}
success
}
}
}
''' % __WISHLIST_ITEM
WISHLIST_REMOVE_QUERY = '''
mutation removeFromWishlistMutation(
$namespace: String!
$offerId: String!
$operation: RemoveOperation!
) {
Wishlist {
removeFromWishlist(
namespace: $namespace
offerId: $offerId
operation: $operation
) {
success
}
}
}
'''
COUPONS_QUERY = '''
query getCoupons(
$currencyCountry: String!
$identityId: String!
$locale: String
) {
CodeRedemption {
coupons(
currencyCountry: $currencyCountry
identityId: $identityId
includeSalesEventInfo: true
) {
code
codeStatus
codeType
consumptionMetadata {
amountDisplay {
amount
currency
placement
symbol
}
minSalesPriceDisplay {
amount
currency
placement
symbol
}
}
endDate
namespace
salesEvent(locale: $locale) {
eventName
eventSlug
voucherImages {
type
url
}
voucherLink
}
startDate
}
}
}
'''
STORE_CONFIG_QUERY = '''
query getStoreConfig(
$includeCriticReviews: Boolean = false
$locale: String!
$sandboxId: String!
$templateId: String
) {
Product {
sandbox(sandboxId: $sandboxId) {
configuration(locale: $locale, templateId: $templateId) {
... on StoreConfiguration {
configs {
shortDescription
criticReviews @include(if: $includeCriticReviews) {
openCritic
}
socialLinks {
platform
url
}
supportedAudio
supportedText
tags(locale: $locale) {
id
name
groupName
}
technicalRequirements {
macos {
minimum
recommended
title
}
windows {
minimum
recommended
title
}
}
}
}
... on HomeConfiguration {
configs {
keyImages {
... on KeyImage {
type
url
alt
}
}
longDescription
}
}
}
}
}
}
'''
def compress_query(query: str) -> str:
return query.replace(" ", "").replace("\n", " ")
game_query = compress_query(SEARCH_STORE_QUERY)
search_query = compress_query(SEARCH_STORE_QUERY)
wishlist_query = compress_query(WISHLIST_QUERY)
wishlist_add_query = compress_query(WISHLIST_ADD_QUERY)
wishlist_remove_query = compress_query(WISHLIST_REMOVE_QUERY)
coupons_query = compress_query(COUPONS_QUERY)
store_config_query = compress_query(STORE_CONFIG_QUERY)
if __name__ == "__main__":
print(SEARCH_STORE_QUERY)

View file

@ -1,271 +0,0 @@
import logging
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QPixmap, QFont, QDesktopServices
from PyQt5.QtWidgets import (
QWidget,
QLabel,
QPushButton,
QHBoxLayout,
QSpacerItem,
QGroupBox,
QTabWidget,
QGridLayout,
)
from rare.components.tabs.store.shop_models import ShopGame
from rare.shared import LegendaryCoreSingleton
from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info
from rare.utils.extra_widgets import ImageLabel
from rare.utils.misc import qta_icon as icon
from rare.widgets.loading_widget import LoadingWidget
logger = logging.getLogger("ShopInfo")
class ShopGameInfo(QWidget, Ui_shop_info):
game: ShopGame
data: dict
# TODO Design
def __init__(self, installed_titles: list, api_core):
super(ShopGameInfo, self).__init__()
self.setupUi(self)
self.core = LegendaryCoreSingleton()
self.api_core = api_core
self.installed = installed_titles
self.open_store_button.clicked.connect(self.button_clicked)
self.image = ImageLabel()
self.image_stack.addWidget(self.image)
self.image_stack.addWidget(LoadingWidget())
warn_label = QLabel()
warn_label.setPixmap(
icon("fa.warning").pixmap(160, 160).scaled(240, 320, Qt.IgnoreAspectRatio)
)
self.image_stack.addWidget(warn_label)
self.wishlist_button.clicked.connect(self.add_to_wishlist)
self.in_wishlist = False
self.wishlist = []
def handle_wishlist_update(self, data):
if data and data[0] == "error":
return
self.wishlist = [i["offer"]["title"] for i in data]
if self.title_str in self.wishlist:
self.in_wishlist = True
self.wishlist_button.setVisible(True)
self.wishlist_button.setText(self.tr("Remove from Wishlist"))
else:
self.in_wishlist = False
self.wishlist_button.setVisible(False)
def update_game(self, data: dict):
self.image_stack.setCurrentIndex(1)
self.title.setText(data["title"])
self.title_str = data["title"]
self.api_core.get_wishlist(self.handle_wishlist_update)
for i in reversed(range(self.req_group_box.layout().count())):
self.req_group_box.layout().itemAt(i).widget().deleteLater()
slug = data["productSlug"]
if not slug:
for mapping in data["offerMappings"]:
if mapping["pageType"] == "productHome":
slug = mapping["pageSlug"]
break
else:
logger.error("Could not get page information")
slug = ""
if "/home" in slug:
slug = slug.replace("/home", "")
self.slug = slug
if data["namespace"] in self.installed:
self.open_store_button.setText(self.tr("Show Game on Epic Page"))
self.owned_label.setVisible(True)
else:
self.open_store_button.setText(self.tr("Buy Game in Epic Games Store"))
self.owned_label.setVisible(False)
for i in range(self.req_group_box.layout().count()):
self.req_group_box.layout().itemAt(i).widget().deleteLater()
self.price.setText(self.tr("Loading"))
self.wishlist_button.setVisible(False)
# self.title.setText(self.tr("Loading"))
self.image.setPixmap(QPixmap())
self.data = data
is_bundle = False
for i in data["categories"]:
if "bundles" in i.get("path", ""):
is_bundle = True
# init API request
if slug:
self.api_core.get_game(slug, is_bundle, self.data_received)
else:
self.data_received({})
def add_to_wishlist(self):
if not self.in_wishlist:
return
# self.api_core.add_to_wishlist(self.game.namespace, self.game.offer_id,
# lambda success: self.wishlist_button.setText(self.tr("Remove from wishlist"))
# if success else self.wishlist_button.setText("Something goes wrong"))
else:
self.api_core.remove_from_wishlist(
self.game.namespace,
self.game.offer_id,
lambda success: self.wishlist_button.setVisible(False)
if success
else self.wishlist_button.setText("Something goes wrong"),
)
def data_received(self, game):
try:
self.game = ShopGame.from_json(game, self.data)
except Exception as e:
logger.error(str(e))
self.price.setText("Error")
self.req_group_box.setVisible(False)
for img in self.data.get("keyImages"):
if img["type"] in [
"DieselStoreFrontWide",
"OfferImageTall",
"VaultClosed",
"ProductLogo",
]:
self.image.update_image(img["url"], self.title_str, size=(240, 320))
self.image_stack.setCurrentIndex(0)
break
else:
self.image_stack.setCurrentIndex(2)
self.price.setText("")
self.discount_price.setText("")
self.social_link_gb.setVisible(False)
self.tags.setText("")
self.dev.setText(self.data.get("seller", {}).get("name", ""))
return
self.title.setText(self.game.title)
self.price.setFont(QFont())
if self.game.price == "0" or self.game.price == 0:
self.price.setText(self.tr("Free"))
else:
self.price.setText(self.game.price)
if self.game.price != self.game.discount_price:
font = QFont()
font.setStrikeOut(True)
self.price.setFont(font)
self.discount_price.setText(
self.game.discount_price
if self.game.discount_price != "0"
else self.tr("Free")
)
self.discount_price.setVisible(True)
else:
self.discount_price.setVisible(False)
bold_font = QFont()
bold_font.setBold(True)
if self.game.reqs:
req_tabs = QTabWidget()
for system in self.game.reqs:
min_label = QLabel(self.tr("Minimum"))
min_label.setFont(bold_font)
rec_label = QLabel(self.tr("Recommend"))
rec_label.setFont(bold_font)
req_widget = QWidget()
req_widget.setLayout(QGridLayout())
req_widget.layout().addWidget(min_label, 0, 1)
req_widget.layout().addWidget(rec_label, 0, 2)
for i, (key, value) in enumerate(
self.game.reqs.get(system, {}).items()
):
req_widget.layout().addWidget(QLabel(key), i + 1, 0)
min_label = QLabel(value[0])
min_label.setWordWrap(True)
req_widget.layout().addWidget(min_label, i + 1, 1)
rec_label = QLabel(value[1])
rec_label.setWordWrap(True)
req_widget.layout().addWidget(rec_label, i + 1, 2)
req_tabs.addTab(req_widget, system)
self.req_group_box.layout().addWidget(req_tabs)
else:
self.req_group_box.layout().addWidget(
QLabel(self.tr("Could not get requirements"))
)
self.req_group_box.setVisible(True)
if self.game.image_urls.front_tall:
img_url = self.game.image_urls.front_tall
elif self.game.image_urls.offer_image_tall:
img_url = self.game.image_urls.offer_image_tall
elif self.game.image_urls.product_logo:
img_url = self.game.image_urls.product_logo
else:
img_url = ""
self.image.update_image(img_url, self.game.title, (240, 320))
self.image_stack.setCurrentIndex(0)
try:
if isinstance(self.game.developer, list):
self.dev.setText(", ".join(self.game.developer))
else:
self.dev.setText(self.game.developer)
except KeyError:
pass
self.tags.setText(", ".join(self.game.tags))
# clear Layout
for widget in (
self.social_link_gb.layout().itemAt(i)
for i in range(self.social_link_gb.layout().count())
):
if not isinstance(widget, QSpacerItem):
widget.widget().deleteLater()
self.social_link_gb.deleteLater()
self.social_link_gb = QGroupBox(self.tr("Social Links"))
self.social_link_gb.setLayout(QHBoxLayout())
self.layout().insertWidget(3, self.social_link_gb)
self.social_link_gb.layout().addStretch(1)
link_count = 0
for name, url in self.game.links:
if name.lower() == "homepage":
icn = icon("mdi.web", "fa.search", scale_factor=1.5)
else:
try:
icn = icon(f"mdi.{name.lower()}", f"fa.{name.lower()}", scale_factor=1.5)
except Exception as e:
logger.error(str(e))
continue
button = SocialButton(icn, url)
self.social_link_gb.layout().addWidget(button)
link_count += 1
self.social_link_gb.layout().addStretch(1)
if link_count == 0:
self.social_link_gb.setVisible(False)
else:
self.social_link_gb.setVisible(True)
self.social_link_gb.layout().addStretch(1)
def add_wishlist_items(self, wishlist):
wishlist = wishlist["data"]["Wishlist"]["wishlistItems"]["elements"]
for game in wishlist:
self.wishlist.append(game["offer"]["title"])
def button_clicked(self):
QDesktopServices.openUrl(QUrl(f"https://www.epicgames.com/store/{self.core.language_code}/p/{self.slug}"))
class SocialButton(QPushButton):
def __init__(self, icn, url):
super(SocialButton, self).__init__(icn, "")
self.url = url
self.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(url)))
self.setToolTip(url)

View file

@ -1,142 +0,0 @@
import logging
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QFont
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from rare.components.tabs.store.shop_models import ImageUrlModel
from rare.ui.components.tabs.store.wishlist_widget import Ui_WishlistWidget
from rare.utils.extra_widgets import ImageLabel
from rare.utils.misc import qta_icon as icon
logger = logging.getLogger("GameWidgets")
class GameWidget(QWidget):
show_info = pyqtSignal(dict)
def __init__(self, path, json_info=None, width=300):
super(GameWidget, self).__init__()
self.manager = QNetworkAccessManager()
self.width = width
self.path = path
if json_info:
self.init_ui(json_info)
def init_ui(self, json_info):
self.layout = QVBoxLayout()
self.image = ImageLabel()
self.layout.addWidget(self.image)
mini_layout = QHBoxLayout()
self.layout.addLayout(mini_layout)
if not json_info:
self.layout.addWidget(QLabel("An error occurred"))
self.setLayout(self.layout)
return
self.title_label = QLabel(json_info.get("title"))
self.title_label.setWordWrap(True)
mini_layout.addWidget(self.title_label)
mini_layout.addStretch(1)
price = json_info["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
discount_price = json_info["price"]["totalPrice"]["fmtPrice"]["discountPrice"]
price_label = QLabel(price)
if price != discount_price:
font = QFont()
font.setStrikeOut(True)
price_label.setFont(font)
mini_layout.addWidget(
QLabel(discount_price if discount_price != "0" else self.tr("Free"))
)
mini_layout.addWidget(price_label)
else:
if price == "0":
price_label.setText(self.tr("Free"))
mini_layout.addWidget(price_label)
for c in r'<>?":|\/*':
json_info["title"] = json_info["title"].replace(c, "")
self.json_info = json_info
self.slug = json_info["productSlug"]
self.title = json_info["title"]
for img in json_info["keyImages"]:
if img["type"] in [
"DieselStoreFrontWide",
"OfferImageWide",
"VaultClosed",
"ProductLogo",
]:
if img["type"] == "VaultClosed" and self.title != "Mystery Game":
continue
self.image.update_image(
img["url"],
json_info["title"],
(self.width, int(self.width * 9 / 16)),
)
break
else:
logger.info(", ".join([img["type"] for img in json_info["keyImages"]]))
self.setLayout(self.layout)
self.setFixedSize(self.width + 10, self.width * 9 // 16 + 50)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
self.show_info.emit(self.json_info)
class WishlistWidget(QWidget, Ui_WishlistWidget):
open_game = pyqtSignal(dict)
delete_from_wishlist = pyqtSignal(dict)
def __init__(self, game: dict):
super(WishlistWidget, self).__init__()
self.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"])
break
else:
self.developer.setText(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
if original_price != discount_price:
self.discount = True
font = QFont()
font.setStrikeOut(True)
self.price.setFont(font)
self.discount_price.setText(discount_price)
else:
self.discount = False
self.discount_price.setVisible(False)
self.image = ImageLabel()
self.layout().insertWidget(0, self.image)
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.delete_button.setIcon(icon("mdi.delete", color="white"))
self.delete_button.clicked.connect(
lambda: self.delete_from_wishlist.emit(self.game)
)
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
# left button
if e.button() == 1:
self.open_game.emit(self.game)
# right
elif e.button() == 2:
pass # self.showMenu(e)

View file

@ -0,0 +1,239 @@
import datetime
import logging
from typing import List
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QObject, QEvent
from PyQt5.QtGui import QShowEvent, QHideEvent, QResizeEvent
from PyQt5.QtWidgets import (
QHBoxLayout,
QWidget,
QSizePolicy,
QVBoxLayout,
QSpacerItem,
QScrollArea,
QFrame,
)
from rare.components.tabs.store.api.models.response import CatalogOfferModel, WishlistItemModel
from rare.widgets.flow_layout import FlowLayout
from rare.widgets.side_tab import SideTabContents
from rare.widgets.sliding_stack import SlidingStackedWidget
from .store_api import StoreAPI
from .widgets.details import DetailsWidget
from .widgets.groups import StoreGroup
from .widgets.items import StoreItemWidget
logger = logging.getLogger("StoreLanding")
class LandingPage(SlidingStackedWidget, SideTabContents):
def __init__(self, store_api: StoreAPI, parent=None):
super(LandingPage, self).__init__(parent=parent)
self.implements_scrollarea = True
self.landing_widget = LandingWidget(store_api, parent=self)
self.landing_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.landing_widget.set_title.connect(self.set_title)
self.landing_widget.show_details.connect(self.show_details)
self.landing_scroll = QScrollArea(self)
self.landing_scroll.setWidgetResizable(True)
self.landing_scroll.setFrameStyle(QFrame.NoFrame | QFrame.Plain)
self.landing_scroll.setWidget(self.landing_widget)
self.landing_scroll.widget().setAutoFillBackground(False)
self.landing_scroll.viewport().setAutoFillBackground(False)
self.details_widget = DetailsWidget([], store_api, parent=self)
self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.details_widget.set_title.connect(self.set_title)
self.details_widget.back_clicked.connect(self.show_main)
self.setDirection(Qt.Horizontal)
self.addWidget(self.landing_scroll)
self.addWidget(self.details_widget)
@pyqtSlot()
def show_main(self):
self.slideInWidget(self.landing_scroll)
@pyqtSlot(object)
def show_details(self, game: CatalogOfferModel):
self.details_widget.update_game(game)
self.slideInWidget(self.details_widget)
class FreeGamesScroll(QScrollArea):
def __init__(self, parent=None):
super(FreeGamesScroll, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
def setWidget(self, w):
super().setWidget(w)
w.installEventFilter(self)
def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
if a0 is self.widget() and a1.type() == QEvent.Resize:
self.__resize(a0)
return a0.event(a1)
return False
def __resize(self, e: QResizeEvent):
minh = self.horizontalScrollBar().minimum()
maxh = self.horizontalScrollBar().maximum()
# lk: when the scrollbar is not visible, min and max are 0
if maxh > minh:
height = (
e.size().height()
+ self.rect().height() // 2
- self.contentsRect().height() // 2
+ self.widget().layout().spacing()
+ self.horizontalScrollBar().sizeHint().height()
)
else:
height = e.size().height() + self.rect().height() - self.contentsRect().height()
self.setMinimumHeight(max(height, self.minimumHeight()))
class LandingWidget(QWidget, SideTabContents):
show_details = pyqtSignal(CatalogOfferModel)
def __init__(self, api: StoreAPI, parent=None):
super(LandingWidget, self).__init__(parent=parent)
self.api = api
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 3, 0)
self.setLayout(layout)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.free_games_now = StoreGroup(self.tr("Free now"), layout=QHBoxLayout, parent=self)
self.free_games_now.main_layout.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.free_games_now.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.free_games_next = StoreGroup(self.tr("Free next week"), layout=QHBoxLayout, parent=self)
self.free_games_next.main_layout.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.free_games_next.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.discounts_group = StoreGroup(self.tr("Wishlist discounts"), layout=FlowLayout, parent=self)
self.discounts_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.games_group = StoreGroup(self.tr("Free to play"), FlowLayout, self)
self.games_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.games_group.loading(False)
self.games_group.setVisible(False)
free_scroll = FreeGamesScroll(self)
free_container = QWidget(free_scroll)
free_scroll.setWidget(free_container)
free_container_layout = QHBoxLayout(free_container)
free_scroll.setWidgetResizable(True)
free_scroll.setFrameShape(QScrollArea.NoFrame)
free_scroll.setSizeAdjustPolicy(QScrollArea.AdjustToContents)
free_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
free_container_layout.setContentsMargins(0, 0, 0, 0)
free_container_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
free_container_layout.setSizeConstraint(QHBoxLayout.SetFixedSize)
free_container_layout.addWidget(self.free_games_now)
free_container_layout.addWidget(self.free_games_next)
free_scroll.widget().setAutoFillBackground(False)
free_scroll.viewport().setAutoFillBackground(False)
# layout.addWidget(self.free_games_now, alignment=Qt.AlignTop)
# layout.addWidget(self.free_games_next, alignment=Qt.AlignTop)
layout.addWidget(free_scroll, alignment=Qt.AlignTop)
layout.addWidget(self.discounts_group, alignment=Qt.AlignTop)
layout.addWidget(self.games_group, alignment=Qt.AlignTop)
layout.addItem(QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding))
def showEvent(self, a0: QShowEvent) -> None:
if a0.spontaneous():
return super().showEvent(a0)
self.api.get_free(self.__update_free_games)
self.api.get_wishlist(self.__update_wishlist_discounts)
return super().showEvent(a0)
def hideEvent(self, a0: QHideEvent) -> None:
if a0.spontaneous():
return super().hideEvent(a0)
# TODO: Implement tab unloading
return super().hideEvent(a0)
def __update_wishlist_discounts(self, wishlist: List[WishlistItemModel]):
for w in self.discounts_group.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly):
self.discounts_group.layout().removeWidget(w)
w.deleteLater()
for item in filter(lambda x: bool(x.offer.price.totalPrice.discount), wishlist):
w = StoreItemWidget(self.api.cached_manager, item.offer)
w.show_details.connect(self.show_details)
self.discounts_group.layout().addWidget(w)
have_discounts = any(map(lambda x: bool(x.offer.price.totalPrice.discount), wishlist))
self.discounts_group.setVisible(have_discounts)
self.discounts_group.loading(False)
def __update_free_games(self, free_games: List[CatalogOfferModel]):
for w in self.free_games_now.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly):
self.free_games_now.layout().removeWidget(w)
w.deleteLater()
for w in self.free_games_next.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly):
self.free_games_next.layout().removeWidget(w)
w.deleteLater()
date = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
free_now = []
free_next = []
for item in free_games:
try:
if item.price.totalPrice.discountPrice == 0:
free_now.append(item)
continue
if item.title == "Mystery Game":
free_next.append(item)
continue
except KeyError as e:
logger.warning(str(e))
if item.promotions is not None:
if not item.promotions.promotionalOffers:
start_date = item.promotions.upcomingPromotionalOffers[0].promotionalOffers[0].startDate
else:
start_date = item.promotions.promotionalOffers[0].promotionalOffers[0].startDate
if start_date > date:
free_next.append(item)
# free games now
self.free_games_now.setVisible(bool(free_now))
for item in free_now:
w = StoreItemWidget(self.api.cached_manager, item)
w.show_details.connect(self.show_details)
self.free_games_now.layout().addWidget(w)
self.free_games_now.loading(False)
# free games next week
self.free_games_next.setVisible(bool(free_next))
for item in free_next:
w = StoreItemWidget(self.api.cached_manager, item)
if item.title != "Mystery Game":
w.show_details.connect(self.show_details)
self.free_games_next.layout().addWidget(w)
self.free_games_next.loading(False)
def show_games(self, data):
if not data:
return
for w in self.games_group.findChildren(StoreItemWidget, options=Qt.FindDirectChildrenOnly):
self.games_group.layout().removeWidget(w)
w.deleteLater()
for game in data:
w = StoreItemWidget(self.api.cached_manager, game)
w.show_details.connect(self.show_details)
self.games_group.layout().addWidget(w)
self.games_group.loading(False)

View file

@ -0,0 +1,56 @@
from PyQt5.QtCore import Qt
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import (
QWidget,
QSizePolicy,
QLabel,
QScrollArea,
)
from rare.widgets.flow_layout import FlowLayout
from .api.models.response import CatalogOfferModel
from .widgets.items import ResultsItemWidget
class ResultsWidget(QScrollArea):
show_details = pyqtSignal(CatalogOfferModel)
def __init__(self, store_api, parent=None):
super(ResultsWidget, self).__init__(parent=parent)
self.store_api = store_api
self.results_container = QWidget(self)
self.results_container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.results_layout = FlowLayout(self.results_container)
self.setWidget(self.results_container)
self.setWidgetResizable(True)
# self.main_layout = QVBoxLayout(self)
# self.main_layout.setContentsMargins(0, 0, 0, 0)
# self.main_layout.addWidget(self.results_scrollarea)
self.setEnabled(False)
def load_results(self, text: str):
self.setEnabled(False)
if text != "":
self.store_api.search_game(text, self.show_results)
def show_results(self, results: dict):
for w in self.results_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
self.results_layout.removeWidget(w)
w.deleteLater()
for w in self.results_container.findChildren(ResultsItemWidget, options=Qt.FindDirectChildrenOnly):
self.results_layout.removeWidget(w)
w.deleteLater()
if not results:
self.results_layout.addWidget(QLabel(self.tr("No results found")))
else:
for res in results:
w = ResultsItemWidget(self.store_api.cached_manager, res, parent=self.results_container)
w.show_details.connect(self.show_details.emit)
self.results_layout.addWidget(w)
self.results_layout.update()
self.setEnabled(True)

View file

@ -0,0 +1,219 @@
import logging
from typing import List
from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot
from PyQt5.QtWidgets import (
QCheckBox,
QWidget,
QSizePolicy,
QScrollArea,
QFrame,
)
from legendary.core import LegendaryCore
from rare.ui.components.tabs.store.search import Ui_SearchWidget
from rare.utils.extra_widgets import ButtonLineEdit
from rare.widgets.side_tab import SideTabContents
from rare.widgets.sliding_stack import SlidingStackedWidget
from .api.models.query import SearchStoreQuery
from .api.models.response import CatalogOfferModel
from .constants import Constants
from .results import ResultsWidget
from .store_api import StoreAPI
from .widgets.details import DetailsWidget
logger = logging.getLogger("Shop")
class SearchPage(SlidingStackedWidget, SideTabContents):
def __init__(self, store_api: StoreAPI, parent=None):
super(SearchPage, self).__init__(parent=parent)
self.implements_scrollarea = True
self.search_widget = SearchWidget(store_api, parent=self)
self.search_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.search_widget.set_title.connect(self.set_title)
self.search_widget.show_details.connect(self.show_details)
self.details_widget = DetailsWidget([], store_api, parent=self)
self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.details_widget.set_title.connect(self.set_title)
self.details_widget.back_clicked.connect(self.show_main)
self.setDirection(Qt.Horizontal)
self.addWidget(self.search_widget)
self.addWidget(self.details_widget)
@pyqtSlot()
def show_main(self):
self.slideInWidget(self.search_widget)
@pyqtSlot(object)
def show_details(self, game: CatalogOfferModel):
self.details_widget.update_game(game)
self.slideInWidget(self.details_widget)
# noinspection PyAttributeOutsideInit,PyBroadException
class SearchWidget(QWidget, SideTabContents):
show_details = pyqtSignal(CatalogOfferModel)
def __init__(self, store_api: StoreAPI, parent=None):
super(SearchWidget, self).__init__(parent=parent)
self.implements_scrollarea = True
self.ui = Ui_SearchWidget()
self.ui.setupUi(self)
self.ui.main_layout.setContentsMargins(0, 0, 3, 0)
self.ui.filter_scrollarea.widget().setAutoFillBackground(False)
self.ui.filter_scrollarea.viewport().setAutoFillBackground(False)
self.store_api = store_api
self.price = ""
self.tags = []
self.types = []
self.update_games_allowed = True
self.active_search_request = False
self.next_search = ""
self.wishlist: List = []
self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search"))
self.results_scrollarea = ResultsWidget(self.store_api, self)
self.results_scrollarea.show_details.connect(self.show_details)
self.ui.left_layout.addWidget(self.search_bar)
self.ui.left_layout.addWidget(self.results_scrollarea)
self.search_bar.returnPressed.connect(self.show_search_results)
self.search_bar.buttonClicked.connect(self.show_search_results)
# self.init_filter()
def load(self):
# load browse games
self.prepare_request()
def show_search_results(self):
if text := self.search_bar.text():
self.results_scrollarea.load_results(text)
# self.show_info.emit(self.search_bar.text())
def init_filter(self):
self.ui.none_price.toggled.connect(
lambda: self.prepare_request("") if self.ui.none_price.isChecked() else None
)
self.ui.free_button.toggled.connect(
lambda: self.prepare_request("free") if self.ui.free_button.isChecked() else None
)
self.ui.under10.toggled.connect(
lambda: self.prepare_request("<price>[0, 1000)") if self.ui.under10.isChecked() else None
)
self.ui.under20.toggled.connect(
lambda: self.prepare_request("<price>[0, 2000)") if self.ui.under20.isChecked() else None
)
self.ui.under30.toggled.connect(
lambda: self.prepare_request("<price>[0, 3000)") if self.ui.under30.isChecked() else None
)
self.ui.above.toggled.connect(
lambda: self.prepare_request("<price>[1499,]") if self.ui.above.isChecked() else None
)
# self.on_discount.toggled.connect(lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None)
self.ui.on_discount.toggled.connect(lambda: self.prepare_request())
constants = Constants()
self.checkboxes = []
for groupbox, variables in [
(self.ui.genre_group, constants.categories),
(self.ui.platform_group, constants.platforms),
(self.ui.others_group, constants.others),
(self.ui.type_group, constants.types),
]:
for text, tag in variables:
checkbox = CheckBox(text, tag)
checkbox.activated.connect(lambda x: self.prepare_request(added_tag=x))
checkbox.deactivated.connect(lambda x: self.prepare_request(removed_tag=x))
groupbox.layout().addWidget(checkbox)
self.checkboxes.append(checkbox)
self.ui.reset_button.clicked.connect(self.reset_filters)
self.ui.filter_scrollarea.setMinimumWidth(
self.ui.filter_container.sizeHint().width()
+ self.ui.filter_container.layout().contentsMargins().left()
+ self.ui.filter_container.layout().contentsMargins().right()
+ self.ui.filter_scrollarea.verticalScrollBar().sizeHint().width()
)
def reset_filters(self):
self.update_games_allowed = False
for cb in self.checkboxes:
cb.setChecked(False)
self.ui.none_price.setChecked(True)
self.tags = []
self.types = []
self.update_games_allowed = True
self.ui.on_discount.setChecked(False)
def prepare_request(
self,
price: str = None,
added_tag: int = 0,
removed_tag: int = 0,
added_type: str = "",
removed_type: str = "",
):
if not self.update_games_allowed:
return
if price is not None:
self.price = price
if added_tag != 0:
self.tags.append(added_tag)
if removed_tag != 0 and removed_tag in self.tags:
self.tags.remove(removed_tag)
if added_type:
self.types.append(added_type)
if removed_type and removed_type in self.types:
self.types.remove(removed_type)
if (self.types or self.price) or self.tags or self.ui.on_discount.isChecked():
# self.free_scrollarea.setVisible(False)
self.discounts_group.setVisible(False)
else:
# self.free_scrollarea.setVisible(True)
if len(self.discounts_group.layout().children()) > 0:
self.discounts_group.setVisible(True)
self.games_group.loading(True)
browse_model = SearchStoreQuery(
language=self.store_api.language_code,
country=self.store_api.country_code,
count=20,
price_range=self.price,
on_sale=self.ui.on_discount.isChecked(),
)
browse_model.tag = "|".join(self.tags)
if self.types:
browse_model.category = "|".join(self.types)
self.store_api.browse_games(browse_model, self.show_games)
class CheckBox(QCheckBox):
activated = pyqtSignal(str)
deactivated = pyqtSignal(str)
def __init__(self, text, tag):
super(CheckBox, self).__init__(text)
self.tag = tag
self.toggled.connect(self.handle_toggle)
def handle_toggle(self):
if self.isChecked():
self.activated.emit(self.tag)
else:
self.deactivated.emit(self.tag)

View file

@ -1,111 +0,0 @@
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QHBoxLayout,
QLabel,
QScrollArea,
QGroupBox,
QPushButton,
QStackedWidget,
)
from rare.utils.extra_widgets import ImageLabel, WaitingSpinner
from rare.widgets.flow_layout import FlowLayout
class SearchResults(QStackedWidget):
show_info = pyqtSignal(dict)
def __init__(self, api_core):
super(SearchResults, self).__init__()
self.search_result_widget = QWidget()
self.api_core = api_core
self.addWidget(self.search_result_widget)
self.main_layout = QVBoxLayout()
self.back_button = QPushButton(self.tr("Back"))
self.main_layout.addWidget(self.back_button)
self.main_layout.addWidget(self.back_button)
self.result_area = QScrollArea()
self.widget = QWidget()
self.result_area.setWidgetResizable(True)
self.main_layout.addWidget(self.result_area)
self.result_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.result_area.setWidget(self.widget)
self.layout = FlowLayout()
self.widget.setLayout(self.layout)
self.search_result_widget.setLayout(self.main_layout)
self.addWidget(WaitingSpinner())
self.setCurrentIndex(1)
def load_results(self, text: str):
self.setCurrentIndex(1)
if text != "":
self.api_core.search_game(text, self.show_results)
def show_results(self, results: dict):
self.widget.deleteLater()
self.widget = QWidget()
self.layout = FlowLayout()
if not results:
self.layout.addWidget(QLabel(self.tr("No results found")))
else:
for res in results:
w = _SearchResultItem(res)
w.show_info.connect(self.show_info.emit)
self.layout.addWidget(w)
self.widget.setLayout(self.layout)
self.result_area.setWidget(self.widget)
self.setCurrentIndex(0)
class _SearchResultItem(QGroupBox):
res: dict
show_info = pyqtSignal(dict)
def __init__(self, result: dict):
super(_SearchResultItem, self).__init__()
self.layout = QVBoxLayout()
self.image = ImageLabel()
for img in result["keyImages"]:
if img["type"] == "DieselStoreFrontTall":
width = 240
self.image.update_image(img["url"], result["title"], (width, 360))
break
else:
print("No image found")
self.layout.addWidget(self.image)
self.res = result
self.title = QLabel(self.res["title"])
title_font = QFont()
title_font.setPixelSize(15)
self.title.setFont(title_font)
self.title.setWordWrap(True)
self.layout.addWidget(self.title)
price = result["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
discount_price = result["price"]["totalPrice"]["fmtPrice"]["discountPrice"]
price_layout = QHBoxLayout()
price_label = QLabel(price if price != "0" else self.tr("Free"))
price_layout.addWidget(price_label)
if price != discount_price:
font = QFont()
font.setStrikeOut(True)
price_label.setFont(font)
price_layout.addWidget(QLabel(discount_price))
# self.discount_price = QLabel(f"{self.tr('Discount price: ')}{discount_price}")
self.layout.addLayout(price_layout)
self.setLayout(self.layout)
self.setFixedWidth(260)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.button() == 1:
self.show_info.emit(self.res)

View file

@ -1,233 +0,0 @@
import urllib.parse
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QObject
from rare.components.tabs.store.constants import (
wishlist_query,
search_query,
add_to_wishlist_query,
remove_from_wishlist_query,
)
from rare.components.tabs.store.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequests
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
class ShopApiCore(QObject):
update_wishlist = pyqtSignal()
def __init__(self, auth_token, lc: str, cc: str):
super(ShopApiCore, self).__init__()
self.token = auth_token
self.language_code: str = lc
self.country_code: str = cc
self.locale = f"{self.language_code}-{self.country_code}"
self.manager = QtRequests(parent=self)
self.auth_manager = QtRequests(token=auth_token, parent=self)
self.browse_active = False
self.next_browse_request = tuple(())
def get_free_games(self, handle_func: callable):
url = f"https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale={self.language_code}&country={self.country_code}&allowCountries={self.country_code}"
self.manager.get(url, lambda data: self._handle_free_games(data, handle_func))
def _handle_free_games(self, data, handle_func):
try:
results: dict = data["data"]["Catalog"]["searchStore"]["elements"]
except KeyError:
logger.error("Free games Api request failed")
handle_func(["error", "Key error"])
return
except Exception as e:
logger.error(f"Free games Api request failed: {e}")
handle_func(["error", e])
return
handle_func(results)
def get_wishlist(self, handle_func):
self.auth_manager.post(
graphql_url,
lambda data: self._handle_wishlist(data, handle_func),
{
"query": wishlist_query,
"variables": {
"country": self.country_code,
"locale": f"{self.language_code}-{self.country_code}",
},
},
)
def _handle_wishlist(self, data, handle_func):
try:
results: list = data["data"]["Wishlist"]["wishlistItems"]["elements"]
except KeyError:
logger.error("Free games Api request failed")
handle_func(["error", "Key error"])
return
except Exception as e:
logger.error(f"Free games Api request failed: {e}")
handle_func(["error", e])
return
handle_func(results)
def search_game(self, name, handle_func):
payload = {
"query": search_query,
"variables": {
"category": "games/edition/base|bundles/games|editors|software/edition/base",
"count": 10,
"country": self.country_code,
"keywords": name,
"locale": self.locale,
"sortDir": "DESC",
"allowCountries": self.country_code,
"start": 0,
"tag": "",
"withMapping": False,
"withPrice": True,
},
}
self.manager.post(
graphql_url, lambda data: self._handle_search(data, handle_func), payload,
)
def _handle_search(self, data, handle_func):
try:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
except KeyError as e:
logger.error(str(e))
handle_func([])
except Exception as e:
logger.error(f"Search Api request failed: {e}")
handle_func([])
return
def browse_games(self, browse_model: BrowseModel, handle_func):
if self.browse_active:
self.next_browse_request = (browse_model, handle_func)
return
self.browse_active = True
url = "https://www.epicgames.com/graphql?operationName=searchStoreQuery&variables={}&extensions={}"
variables = urllib.parse.quote_plus(str(
dict(browse_model.__dict__))
)
extensions = urllib.parse.quote_plus(str(
dict(
persistedQuery=dict(
version=1,
sha256Hash="6e7c4dd0177150eb9a47d624be221929582df8648e7ec271c821838ff4ee148e"
)
)
)
)
for old, new in [
("%26", "&"),
("%27", "%22"),
("+", ""),
("%3A", ":"),
("%2C", ","),
("%5B", "["),
("%5D", "]"),
("True", "true"),
]:
variables = variables.replace(old, new)
extensions = extensions.replace(old, new)
url = url.format(variables, extensions)
self.auth_manager.get(
url, lambda data: self._handle_browse_games(data, handle_func)
)
def _handle_browse_games(self, data, handle_func):
self.browse_active = False
if data is None:
data = {}
if not self.next_browse_request:
try:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
except KeyError as e:
logger.error(str(e))
handle_func([])
except Exception as e:
logger.error(f"Browse games Api request failed: {e}")
handle_func([])
return
else:
self.browse_games(*self.next_browse_request) # pylint: disable=E1120
self.next_browse_request = tuple(())
def get_game(self, slug: str, is_bundle: bool, handle_func):
url = f"https://store-content.ak.epicgames.com/api/{self.locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}"
self.manager.get(url, lambda data: self._handle_get_game(data, handle_func))
def _handle_get_game(self, data, handle_func):
try:
handle_func(data)
except Exception as e:
logger.error(str(e))
handle_func({})
# needs a captcha
def add_to_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"variables": {
"offerId": offer_id,
"namespace": namespace,
"country": self.country_code,
"locale": self.locale,
},
"query": add_to_wishlist_query,
}
self.auth_manager.post(
graphql_url,
lambda data: self._handle_add_to_wishlist(data, handle_func),
payload,
)
def _handle_add_to_wishlist(self, data, handle_func):
try:
data = data["data"]["Wishlist"]["addToWishlist"]
if data["success"]:
handle_func(True)
else:
handle_func(False)
except Exception as e:
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()
def remove_from_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"variables": {
"offerId": offer_id,
"namespace": namespace,
"operation": "REMOVE",
},
"query": remove_from_wishlist_query,
}
self.auth_manager.post(
graphql_url,
lambda data: self._handle_remove_from_wishlist(data, handle_func),
payload,
)
def _handle_remove_from_wishlist(self, data, handle_func):
try:
data = data["data"]["Wishlist"]["removeFromWishlist"]
if data["success"]:
handle_func(True)
else:
handle_func(False)
except Exception as e:
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()

View file

@ -1,184 +0,0 @@
import datetime
from dataclasses import dataclass
class ImageUrlModel:
def __init__(
self,
front_tall: str = "",
offer_image_tall: str = "",
thumbnail: str = "",
front_wide: str = "",
offer_image_wide: str = "",
product_logo: str = "",
):
self.front_tall = front_tall
self.offer_image_tall = offer_image_tall
self.thumbnail = thumbnail
self.front_wide = front_wide
self.offer_image_wide = offer_image_wide
self.product_logo = product_logo
@classmethod
def from_json(cls, json_data: list):
tmp = cls()
for item in json_data:
if item["type"] == "Thumbnail":
tmp.thumbnail = item["url"]
elif item["type"] == "DieselStoreFrontTall":
tmp.front_tall = item["url"]
elif item["type"] == "DieselStoreFrontWide":
tmp.front_wide = item["url"]
elif item["type"] == "OfferImageTall":
tmp.offer_image_tall = item["url"]
elif item["type"] == "OfferImageWide":
tmp.offer_image_wide = item["url"]
elif item["type"] == "ProductLogo":
tmp.product_logo = item["url"]
return tmp
class ShopGame:
# TODO: Copyrights etc
def __init__(
self,
title: str = "",
image_urls: ImageUrlModel = None,
social_links: dict = None,
langs: list = None,
reqs: dict = None,
publisher: str = "",
developer: str = "",
original_price: str = "",
discount_price: str = "",
tags: list = None,
namespace: str = "",
offer_id: str = "",
):
self.title = title
self.image_urls = image_urls
self.links = []
if social_links:
for item in social_links:
if item.startswith("link"):
self.links.append(
tuple((item.replace("link", ""), social_links[item]))
)
else:
self.links = []
self.languages = langs
self.reqs = reqs
self.publisher = publisher
self.developer = developer
self.price = original_price
self.discount_price = discount_price
self.tags = tags
self.namespace = namespace
self.offer_id = offer_id
@classmethod
def from_json(cls, api_data: dict, search_data: dict):
if isinstance(api_data, list):
for product in api_data:
if product["_title"] == "home":
api_data = product
break
if "pages" in api_data.keys():
for page in api_data["pages"]:
if page["_slug"] == "home":
api_data = page
break
tmp = cls()
tmp.title = search_data.get("title", "Fail")
tmp.image_urls = ImageUrlModel.from_json(search_data["keyImages"])
links = api_data["data"]["socialLinks"]
tmp.links = []
for item in links:
if item.startswith("link"):
tmp.links.append(tuple((item.replace("link", ""), links[item])))
tmp.available_voice_langs = api_data["data"]["requirements"].get(
"languages", "Failed"
)
tmp.reqs = {}
for i, system in enumerate(api_data["data"]["requirements"].get("systems", [])):
try:
tmp.reqs[system["systemType"]] = {}
except KeyError:
continue
for req in system["details"]:
try:
tmp.reqs[system["systemType"]][req["title"]] = (
req["minimum"],
req["recommended"],
)
except KeyError:
pass
tmp.publisher = api_data["data"]["meta"].get("publisher", "")
tmp.developer = api_data["data"]["meta"].get("developer", "")
if not tmp.developer:
for i in search_data["customAttributes"]:
if i["key"] == "developerName":
tmp.developer = i["value"]
tmp.price = search_data["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
tmp.discount_price = search_data["price"]["totalPrice"]["fmtPrice"][
"discountPrice"
]
tmp.tags = [
i.replace("_", " ").capitalize()
for i in api_data["data"]["meta"].get("tags", [])
]
tmp.namespace = search_data["namespace"]
tmp.offer_id = search_data["id"]
return tmp
@dataclass
class BrowseModel:
category: str = "games/edition/base|bundles/games|editors|software/edition/base"
count: int = 30
language_code: str = "en"
country_code: str = "US"
keywords: str = ""
sortDir: str = "DESC"
start: int = 0
tag: str = ""
withMapping: bool = True
withPrice: bool = True
date: str = (
f"[,{datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%dT%H:%M:%S')}.420Z]"
)
price: str = ""
onSale: bool = False
@property
def __dict__(self):
payload = {
"allowCountries": self.country_code,
"category": self.category,
"count": self.count,
"country": self.country_code,
"keywords": self.keywords,
"locale": self.language_code,
"priceRange": self.price,
"releaseDate": self.date,
"sortBy": "releaseDate",
"sortDir": self.sortDir,
"start": self.start,
"tag": self.tag,
"withPrice": self.withPrice,
}
if self.price == "free":
payload["freeGame"] = True
payload.pop("priceRange")
elif self.price.startswith("<price>"):
payload["priceRange"] = self.price.replace("<price>", "")
if self.onSale:
payload["onSale"] = True
if self.price:
payload["effectiveDate"] = self.date
else:
payload.pop("priceRange")
return payload

View file

@ -1,367 +0,0 @@
import datetime
import logging
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import (
QGroupBox,
QScrollArea,
QCheckBox,
QLabel,
QPushButton,
QHBoxLayout,
)
from legendary.core import LegendaryCore
from rare.ui.components.tabs.store.store import Ui_ShopWidget
from rare.utils.extra_widgets import WaitingSpinner, ButtonLineEdit
from rare.widgets.flow_layout import FlowLayout
from .constants import Constants
from .game_widgets import GameWidget
from .shop_api_core import ShopApiCore
from .shop_models import BrowseModel
logger = logging.getLogger("Shop")
# noinspection PyAttributeOutsideInit,PyBroadException
class ShopWidget(QScrollArea, Ui_ShopWidget):
show_info = pyqtSignal(str)
show_game = pyqtSignal(dict)
free_game_widgets = []
active_search_request = False
next_search = ""
wishlist: list = []
def __init__(self, path, core: LegendaryCore, shop_api: ShopApiCore):
super(ShopWidget, self).__init__()
self.setWidgetResizable(True)
self.setupUi(self)
self.path = path
self.core = core
self.api_core = shop_api
self.price = ""
self.tags = []
self.types = []
self.update_games_allowed = True
self.free_widget.setLayout(FlowLayout())
self.free_stack.addWidget(WaitingSpinner())
self.free_stack.setCurrentIndex(1)
self.discount_widget.setLayout(FlowLayout())
self.discount_stack.addWidget(WaitingSpinner())
self.discount_stack.setCurrentIndex(1)
self.game_widget.setLayout(FlowLayout())
self.game_stack.addWidget(WaitingSpinner())
self.game_stack.setCurrentIndex(1)
self.search_bar = ButtonLineEdit(
"fa.search", placeholder_text=self.tr("Search Games")
)
self.layout().insertWidget(0, self.search_bar)
# self.search_bar.textChanged.connect(self.search_games)
self.search_bar.returnPressed.connect(self.show_search_results)
self.search_bar.buttonClicked.connect(self.show_search_results)
self.init_filter()
self.search_bar.setHidden(True)
self.filter_gb.setHidden(True)
self.filter_game_gb.setHidden(True)
# self.search_bar.textChanged.connect(self.load_completer)
def load(self):
# load free games
self.api_core.get_free_games(self.add_free_games)
# load wishlist
self.api_core.get_wishlist(self.add_wishlist_items)
# load browse games
self.prepare_request()
def update_wishlist(self):
self.api_core.get_wishlist(self.add_wishlist_items)
def add_wishlist_items(self, wishlist):
for i in range(self.discount_widget.layout().count()):
item = self.discount_widget.layout().itemAt(i)
if item:
item.widget().deleteLater()
if wishlist and wishlist[0] == "error":
self.discount_widget.layout().addWidget(
QLabel(self.tr("Failed to get wishlist: {}").format(wishlist[1]))
)
btn = QPushButton(self.tr("Reload"))
self.discount_widget.layout().addWidget(btn)
btn.clicked.connect(
lambda: self.api_core.get_wishlist(self.add_wishlist_items)
)
self.discount_stack.setCurrentIndex(0)
return
discounts = 0
for game in wishlist:
if not game:
continue
try:
if game["offer"]["price"]["totalPrice"]["discount"] > 0:
w = GameWidget(self.path, game["offer"])
w.show_info.connect(self.show_game.emit)
self.discount_widget.layout().addWidget(w)
discounts += 1
except Exception as e:
logger.warning(f"{game} {e}")
continue
self.discounts_gb.setVisible(discounts > 0)
self.discount_stack.setCurrentIndex(0)
# fix widget overlay
self.discount_widget.layout().update()
def add_free_games(self, free_games: list):
for i in range(self.free_widget.layout().count()):
item = self.free_widget.layout().itemAt(i)
if item:
item.widget().deleteLater()
if free_games and free_games[0] == "error":
self.free_widget.layout().addWidget(
QLabel(self.tr("Failed to fetch free games: {}").format(free_games[1]))
)
btn = QPushButton(self.tr("Reload"))
self.free_widget.layout().addWidget(btn)
btn.clicked.connect(
lambda: self.api_core.get_free_games(self.add_free_games)
)
self.free_stack.setCurrentIndex(0)
return
self.free_games_now = QGroupBox(self.tr("Now Free"))
self.free_games_now.setLayout(QHBoxLayout())
self.free_widget.layout().addWidget(self.free_games_now)
self.coming_free_games = QGroupBox(self.tr("Free Games next week"))
self.coming_free_games.setLayout(QHBoxLayout())
self.free_widget.layout().addWidget(self.coming_free_games)
date = datetime.datetime.now()
free_games_now = []
coming_free_games = []
for game in free_games:
try:
if (
game["price"]["totalPrice"]["fmtPrice"]["discountPrice"] == "0"
and game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
!= game["price"]["totalPrice"]["fmtPrice"]["discountPrice"]
):
free_games_now.append(game)
continue
if game["title"] == "Mystery Game":
coming_free_games.append(game)
continue
except KeyError as e:
logger.warning(str(e))
try:
# parse datetime to check if game is next week or now
try:
start_date = datetime.datetime.strptime(
game["promotions"]["upcomingPromotionalOffers"][0][
"promotionalOffers"
][0]["startDate"],
"%Y-%m-%dT%H:%M:%S.%fZ",
)
except Exception:
try:
start_date = datetime.datetime.strptime(
game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["startDate"],
"%Y-%m-%dT%H:%M:%S.%fZ",
)
except Exception as e:
continue
except TypeError:
print("type error")
continue
if start_date > date:
coming_free_games.append(game)
# free games now
now_free = 0
for free_game in free_games_now:
w = GameWidget(self.path, free_game)
w.show_info.connect(self.show_game.emit)
self.free_games_now.layout().addWidget(w)
self.free_game_widgets.append(w)
now_free += 1
if now_free == 0:
self.free_games_now.layout().addWidget(
QLabel(self.tr("Could not find current free game"))
)
# free games next week
for free_game in coming_free_games:
w = GameWidget(self.path, free_game)
if free_game["title"] != "Mystery Game":
w.show_info.connect(self.show_game.emit)
self.coming_free_games.layout().addWidget(w)
# self.coming_free_games.setFixedWidth(int(40 + len(coming_free_games) * 300))
self.free_stack.setCurrentIndex(0)
def show_search_results(self):
if self.search_bar.text():
self.show_info.emit(self.search_bar.text())
def init_filter(self):
self.none_price.toggled.connect(
lambda: self.prepare_request("") if self.none_price.isChecked() else None
)
self.free_button.toggled.connect(
lambda: self.prepare_request("free")
if self.free_button.isChecked()
else None
)
self.under10.toggled.connect(
lambda: self.prepare_request("<price>[0, 1000)")
if self.under10.isChecked()
else None
)
self.under20.toggled.connect(
lambda: self.prepare_request("<price>[0, 2000)")
if self.under20.isChecked()
else None
)
self.under30.toggled.connect(
lambda: self.prepare_request("<price>[0, 3000)")
if self.under30.isChecked()
else None
)
self.above.toggled.connect(
lambda: self.prepare_request("<price>[1499,]")
if self.above.isChecked()
else None
)
# self.on_discount.toggled.connect(lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None)
self.on_discount.toggled.connect(lambda: self.prepare_request())
constants = Constants()
self.checkboxes = []
for groupbox, variables in [
(self.genre_gb, constants.categories),
(self.platform_gb, constants.platforms),
(self.others_gb, constants.others),
(self.type_gb, constants.types),
]:
for text, tag in variables:
checkbox = CheckBox(text, tag)
checkbox.activated.connect(lambda x: self.prepare_request(added_tag=x))
checkbox.deactivated.connect(
lambda x: self.prepare_request(removed_tag=x)
)
groupbox.layout().addWidget(checkbox)
self.checkboxes.append(checkbox)
self.reset_button.clicked.connect(self.reset_filters)
def reset_filters(self):
self.update_games_allowed = False
for cb in self.checkboxes:
cb.setChecked(False)
self.none_price.setChecked(True)
self.tags = []
self.types = []
self.update_games_allowed = True
self.prepare_request("")
self.on_discount.setChecked(False)
def prepare_request(
self,
price: str = None,
added_tag: int = 0,
removed_tag: int = 0,
added_type: str = "",
removed_type: str = "",
):
if not self.update_games_allowed:
return
if price is not None:
self.price = price
if added_tag != 0:
self.tags.append(added_tag)
if removed_tag != 0 and removed_tag in self.tags:
self.tags.remove(removed_tag)
if added_type:
self.types.append(added_type)
if removed_type and removed_type in self.types:
self.types.remove(removed_type)
if (self.types or self.price) or self.tags or self.on_discount.isChecked():
self.free_game_group_box.setVisible(False)
self.discounts_gb.setVisible(False)
else:
self.free_game_group_box.setVisible(True)
if len(self.discounts_gb.layout().children()) > 0:
self.discounts_gb.setVisible(True)
self.game_stack.setCurrentIndex(1)
browse_model = BrowseModel(
language_code=self.core.language_code,
country_code=self.core.country_code,
count=20,
price=self.price,
onSale=self.on_discount.isChecked(),
)
browse_model.tag = "|".join(self.tags)
if self.types:
browse_model.category = "|".join(self.types)
self.api_core.browse_games(browse_model, self.show_games)
def show_games(self, data):
for item in (
self.game_widget.layout().itemAt(i)
for i in range(self.game_widget.layout().count())
):
item.widget().deleteLater()
if data:
for game in data:
w = GameWidget(self.path, game, 275)
self.game_widget.layout().addWidget(w)
w.show_info.connect(self.show_game.emit)
else:
self.game_widget.layout().addWidget(
QLabel(self.tr("Could not get games matching the filter"))
)
self.game_stack.setCurrentIndex(0)
self.game_widget.layout().update()
class CheckBox(QCheckBox):
activated = pyqtSignal(str)
deactivated = pyqtSignal(str)
def __init__(self, text, tag):
super(CheckBox, self).__init__(text)
self.tag = tag
self.toggled.connect(self.handle_toggle)
def handle_toggle(self):
if self.isChecked():
self.activated.emit(self.tag)
else:
self.deactivated.emit(self.tag)

View file

@ -0,0 +1,258 @@
from logging import getLogger
from typing import List, Callable
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication
from rare.components.tabs.store.constants import (
wishlist_query,
search_query,
wishlist_add_query,
wishlist_remove_query,
)
from rare.utils.paths import cache_dir
from rare.utils.qt_requests import QtRequests
from .api.models.query import SearchStoreQuery
from .api.models.diesel import DieselProduct
from .api.models.response import (
ResponseModel,
CatalogOfferModel,
)
logger = getLogger("StoreAPI")
graphql_url = "https://graphql.epicgames.com/graphql"
DEBUG: Callable[[], bool] = lambda: "--debug" in QApplication.arguments()
class StoreAPI(QObject):
update_wishlist = pyqtSignal()
def __init__(self, token, language: str, country: str, installed):
super(StoreAPI, self).__init__()
self.token = token
self.language_code: str = language
self.country_code: str = country
self.locale = f"{self.language_code}-{self.country_code}"
self.locale = "en-US"
self.manager = QtRequests(parent=self)
self.authed_manager = QtRequests(token=token, parent=self)
self.cached_manager = QtRequests(cache=str(cache_dir().joinpath("store")), parent=self)
self.installed = installed
self.browse_active = False
self.next_browse_request = tuple(())
def get_free(self, handle_func: callable):
url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions"
params = {
"locale": self.locale,
"country": self.country_code,
"allowCountries": self.country_code,
}
self.manager.get(url, lambda data: self.__handle_free_games(data, handle_func), params=params)
@staticmethod
def __handle_free_games(data, handle_func):
try:
response = ResponseModel.from_dict(data)
results: List[CatalogOfferModel] = response.data.catalog.searchStore.elements
handle_func(results)
except KeyError as e:
if DEBUG():
raise e
logger.error("Free games Api request failed")
handle_func(["error", "Key error"])
return
except Exception as e:
if DEBUG():
raise e
logger.error(f"Free games Api request failed: {e}")
handle_func(["error", e])
return
def get_wishlist(self, handle_func):
self.authed_manager.post(
graphql_url,
lambda data: self.__handle_wishlist(data, handle_func),
{
"query": wishlist_query,
"variables": {
"country": self.country_code,
"locale": self.locale,
"withPrice": True,
},
},
)
@staticmethod
def __handle_wishlist(data, handle_func):
try:
response = ResponseModel.from_dict(data)
if response.errors:
logger.error(response.errors)
handle_func(response.data.wishlist.wishlistItems.elements)
except KeyError as e:
if DEBUG():
raise e
logger.error("Free games Api request failed")
handle_func(["error", "Key error"])
return
except Exception as e:
if DEBUG():
raise e
logger.error(f"Free games Api request failed: {e}")
handle_func(["error", e])
return
def search_game(self, name, handler):
payload = {
"query": search_query,
"variables": {
"category": "games/edition/base|bundles/games|editors|software/edition/base",
"count": 20,
"country": self.country_code,
"keywords": name,
"locale": self.locale,
"sortDir": "DESC",
"allowCountries": self.country_code,
"start": 0,
"tag": "",
"withMapping": False,
"withPrice": True,
},
}
self.manager.post(graphql_url, lambda data: self.__handle_search(data, handler), payload)
@staticmethod
def __handle_search(data, handler):
try:
response = ResponseModel.from_dict(data)
handler(response.data.catalog.searchStore.elements)
except KeyError as e:
logger.error(str(e))
if DEBUG():
raise e
handler([])
except Exception as e:
logger.error(f"Search Api request failed: {e}")
if DEBUG():
raise e
handler([])
return
def browse_games(self, browse_model: SearchStoreQuery, handle_func):
if self.browse_active:
self.next_browse_request = (browse_model, handle_func)
return
self.browse_active = True
payload = {
"query": search_query,
"variables": browse_model.to_dict()
}
self.manager.post(graphql_url, lambda data: self.__handle_browse_games(data, handle_func), payload)
def __handle_browse_games(self, data, handle_func):
self.browse_active = False
if data is None:
data = {}
if not self.next_browse_request:
try:
response = ResponseModel.from_dict(data)
handle_func(response.data.catalog.searchStore.elements)
except KeyError as e:
if DEBUG():
raise e
logger.error(str(e))
handle_func([])
except Exception as e:
if DEBUG():
raise e
logger.error(f"Browse games Api request failed: {e}")
handle_func([])
return
else:
self.browse_games(*self.next_browse_request) # pylint: disable=E1120
self.next_browse_request = tuple(())
# def get_game_config_graphql(self, namespace: str, handle_func):
# payload = {
# "query": config_query,
# "variables": {
# "namespace": namespace
# }
# }
def __make_graphql_query(self):
pass
def __make_api_query(self):
pass
def get_game_config_cms(self, slug: str, is_bundle: bool, handle_func):
url = "https://store-content.ak.epicgames.com/api"
url += f"/{self.locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}"
self.manager.get(url, lambda data: self.__handle_get_game(data, handle_func))
@staticmethod
def __handle_get_game(data, handle_func):
try:
product = DieselProduct.from_dict(data)
handle_func(product)
except Exception as e:
if DEBUG():
raise e
logger.error(str(e))
# handle_func({})
# needs a captcha
def add_to_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"query": wishlist_add_query,
"variables": {
"offerId": offer_id,
"namespace": namespace,
"country": self.country_code,
"locale": self.locale,
},
}
self.authed_manager.post(graphql_url, lambda data: self._handle_add_to_wishlist(data, handle_func), payload)
def _handle_add_to_wishlist(self, data, handle_func):
try:
response = ResponseModel.from_dict(data)
data = response.data.wishlist.addToWishlist
handle_func(data.success)
except Exception as e:
if DEBUG():
raise e
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()
def remove_from_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"query": wishlist_remove_query,
"variables": {
"offerId": offer_id,
"namespace": namespace,
"operation": "REMOVE",
},
}
self.authed_manager.post(graphql_url, lambda data: self._handle_remove_from_wishlist(data, handle_func),
payload)
def _handle_remove_from_wishlist(self, data, handle_func):
try:
response = ResponseModel.from_dict(data)
data = response.data.wishlist.removeFromWishlist
handle_func(data.success)
except Exception as e:
if DEBUG():
raise e
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()

View file

@ -0,0 +1,269 @@
import logging
from typing import List, Dict
from PyQt5.QtCore import Qt, QUrl, pyqtSignal
from PyQt5.QtGui import QFont, QDesktopServices, QKeyEvent
from PyQt5.QtWidgets import (
QWidget,
QLabel,
QPushButton,
QGridLayout,
QSizePolicy,
)
from rare.components.tabs.store.api.models.diesel import DieselProduct, DieselProductDetail, DieselSystemDetail
from rare.components.tabs.store.api.models.response import CatalogOfferModel
from rare.components.tabs.store.store_api import StoreAPI
from rare.models.image import ImageSize
from rare.ui.components.tabs.store.details import Ui_DetailsWidget
from rare.utils.misc import qta_icon
from rare.widgets.elide_label import ElideLabel
from rare.widgets.side_tab import SideTabWidget, SideTabContents
from .image import LoadingImageWidget
logger = logging.getLogger("StoreDetails")
class DetailsWidget(QWidget, SideTabContents):
back_clicked: pyqtSignal = pyqtSignal()
# TODO Design
def __init__(self, installed: List, store_api: StoreAPI, parent=None):
super(DetailsWidget, self).__init__(parent=parent)
self.implements_scrollarea = True
self.ui = Ui_DetailsWidget()
self.ui.setupUi(self)
self.ui.main_layout.setContentsMargins(0, 0, 3, 0)
self.store_api = store_api
self.installed = installed
self.catalog_offer: CatalogOfferModel = None
self.image = LoadingImageWidget(store_api.cached_manager, self)
self.image.setFixedSize(ImageSize.Display)
self.ui.left_layout.insertWidget(0, self.image, alignment=Qt.AlignTop)
self.ui.left_layout.setAlignment(Qt.AlignTop)
self.ui.wishlist_button.clicked.connect(self.add_to_wishlist)
self.ui.store_button.clicked.connect(self.button_clicked)
self.ui.wishlist_button.setVisible(True)
self.in_wishlist = False
self.wishlist = []
self.requirements_tabs = SideTabWidget(parent=self.ui.requirements_frame)
self.requirements_tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.ui.requirements_layout.addWidget(self.requirements_tabs)
self.ui.back_button.setIcon(qta_icon("fa.chevron-left"))
self.ui.back_button.clicked.connect(self.back_clicked)
self.setDisabled(False)
def handle_wishlist_update(self, wishlist: List[CatalogOfferModel]):
if wishlist and wishlist[0] == "error":
return
self.wishlist = [game.id for game in wishlist]
if self.id_str in self.wishlist:
self.in_wishlist = True
self.ui.wishlist_button.setText(self.tr("Remove from Wishlist"))
else:
self.in_wishlist = False
def update_game(self, offer: CatalogOfferModel):
self.ui.title.setText(offer.title)
self.title_str = offer.title
self.id_str = offer.id
self.store_api.get_wishlist(self.handle_wishlist_update)
# lk: delete tabs in reverse order because indices are updated on deletion
while self.requirements_tabs.count():
self.requirements_tabs.widget(0).deleteLater()
self.requirements_tabs.removeTab(0)
self.requirements_tabs.clear()
slug = offer.productSlug
if not slug:
for mapping in offer.offerMappings:
if mapping["pageType"] == "productHome":
slug = mapping["pageSlug"]
break
else:
logger.error("Could not get page information")
slug = ""
if "/home" in slug:
slug = slug.replace("/home", "")
self.slug = slug
if offer.namespace in self.installed:
self.ui.store_button.setText(self.tr("Show Game on Epic Page"))
self.ui.status.setVisible(True)
else:
self.ui.store_button.setText(self.tr("Buy Game in Epic Games Store"))
self.ui.status.setVisible(False)
self.ui.original_price.setText(self.tr("Loading"))
# self.title.setText(self.tr("Loading"))
# self.image.setPixmap(QPixmap())
is_bundle = False
for i in offer.categories:
if "bundles" in i.get("path", ""):
is_bundle = True
# init API request
if slug:
self.store_api.get_game_config_cms(offer.productSlug, is_bundle, self.data_received)
# else:
# self.data_received({})
self.catalog_offer = offer
def add_to_wishlist(self):
if not self.in_wishlist:
self.store_api.add_to_wishlist(
self.catalog_offer.namespace,
self.catalog_offer.id,
lambda success: self.ui.wishlist_button.setText(self.tr("Remove from wishlist"))
if success
else self.ui.wishlist_button.setText("Something went wrong")
)
else:
self.store_api.remove_from_wishlist(
self.catalog_offer.namespace,
self.catalog_offer.id,
lambda success: self.ui.wishlist_button.setText(self.tr("Add to wishlist"))
if success
else self.ui.wishlist_button.setText("Something went wrong"),
)
def data_received(self, product: DieselProduct):
try:
if product.pages:
product_data: DieselProductDetail = product.pages[0].data
else:
product_data: DieselProductDetail = product.data
except Exception as e:
raise e
logger.error(str(e))
self.ui.original_price.setFont(self.font())
price = self.catalog_offer.price.totalPrice.fmtPrice["originalPrice"]
discount_price = self.catalog_offer.price.totalPrice.fmtPrice["discountPrice"]
if price == "0" or price == 0:
self.ui.original_price.setText(self.tr("Free"))
else:
self.ui.original_price.setText(price)
if price != discount_price:
font = self.font()
font.setStrikeOut(True)
self.ui.original_price.setFont(font)
self.ui.discount_price.setText(
discount_price
if discount_price != "0"
else self.tr("Free")
)
self.ui.discount_price.setVisible(True)
else:
self.ui.discount_price.setVisible(False)
requirements = product_data.requirements
if requirements and requirements.systems:
for system in requirements.systems:
req_widget = RequirementsWidget(system, self.requirements_tabs)
self.requirements_tabs.addTab(req_widget, system.systemType)
self.ui.requirements_frame.setVisible(True)
else:
self.ui.requirements_frame.setVisible(False)
key_images = self.catalog_offer.keyImages
img_url = key_images.for_dimensions(self.image.size().width(), self.image.size().height())
self.image.fetchPixmap(img_url.url)
# self.image_stack.setCurrentIndex(0)
about = product_data.about
self.ui.description_label.setMarkdown(about.desciption)
self.ui.developer.setText(about.developerAttribution)
# try:
# if isinstance(aboudeveloper, list):
# self.ui.dev.setText(", ".join(self.game.developer))
# else:
# self.ui.dev.setText(self.game.developer)
# except KeyError:
# pass
tags = product_data.unmapped["meta"].get("tags", [])
self.ui.tags.setText(", ".join(tags))
# clear Layout
for b in self.ui.social_links.findChildren(SocialButton, options=Qt.FindDirectChildrenOnly):
self.ui.social_links_layout.removeWidget(b)
b.deleteLater()
links = product_data.socialLinks
link_count = 0
for name, url in links.items():
if name == "_type":
continue
name = name.replace("link", "").lower()
if name == "homepage":
icn = qta_icon("mdi.web", "fa.search", scale_factor=1.5)
else:
try:
icn = qta_icon(f"mdi.{name}", f"fa.{name}", scale_factor=1.5)
except Exception as e:
logger.error(str(e))
continue
button = SocialButton(icn, url, parent=self.ui.social_links)
self.ui.social_links_layout.addWidget(button)
link_count += 1
self.ui.social_links.setEnabled(bool(link_count))
self.setEnabled(True)
# def add_wishlist_items(self, wishlist: List[CatalogGameModel]):
# wishlist = wishlist["data"]["Wishlist"]["wishlistItems"]["elements"]
# for game in wishlist:
# self.wishlist.append(game["offer"]["title"])
def button_clicked(self):
QDesktopServices.openUrl(QUrl(f"https://www.epicgames.com/store/{self.store_api.language_code}/p/{self.slug}"))
def keyPressEvent(self, a0: QKeyEvent):
if a0.key() == Qt.Key_Escape:
self.back_clicked.emit()
class SocialButton(QPushButton):
def __init__(self, icn, url, parent=None):
super(SocialButton, self).__init__(icn, "", parent=parent)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.url = url
self.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(url)))
self.setToolTip(url)
class RequirementsWidget(QWidget, SideTabContents):
def __init__(self, system: DieselSystemDetail, parent=None):
super().__init__(parent=parent)
self.implements_scrollarea = True
bold_font = self.font()
bold_font.setBold(True)
req_layout = QGridLayout(self)
min_label = QLabel(self.tr("Minimum"), parent=self)
min_label.setFont(bold_font)
rec_label = QLabel(self.tr("Recommend"), parent=self)
rec_label.setFont(bold_font)
req_layout.addWidget(min_label, 0, 1)
req_layout.addWidget(rec_label, 0, 2)
req_layout.setColumnStretch(1, 2)
req_layout.setColumnStretch(2, 2)
for i, detail in enumerate(system.details):
req_layout.addWidget(QLabel(detail.title, parent=self), i + 1, 0)
min_label = ElideLabel(detail.minimum, parent=self)
req_layout.addWidget(min_label, i + 1, 1)
rec_label = ElideLabel(detail.recommended, parent=self)
req_layout.addWidget(rec_label, i + 1, 2)
req_layout.setAlignment(Qt.AlignTop)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

View file

@ -0,0 +1,18 @@
from PyQt5.QtWidgets import QGroupBox, QLayout
from rare.widgets.loading_widget import LoadingWidget
class StoreGroup(QGroupBox):
def __init__(self, title: str, layout: type[QLayout], parent=None):
super().__init__(parent=parent)
self.setTitle(title)
self.main_layout = layout(self)
self.loading_widget = LoadingWidget(autostart=True, parent=self)
def loading(self, state: bool) -> None:
if state:
self.loading_widget.start()
else:
self.loading_widget.stop()

View file

@ -0,0 +1,112 @@
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
QPixmap,
QImage,
)
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QSpacerItem,
QSizePolicy,
QHBoxLayout,
QLabel,
)
from rare.utils.qt_requests import QtRequests
from rare.widgets.image_widget import ImageWidget
from rare.widgets.loading_widget import LoadingWidget
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(int(widget.height() // 3))
# 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 LoadingImageWidget(ImageWidget):
def __init__(self, manager: QtRequests, parent=None):
super(LoadingImageWidget, self).__init__(parent=parent)
self.ui = IconWidget()
self.spinner = LoadingWidget(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()

View file

@ -0,0 +1,136 @@
import logging
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QPushButton
from rare.components.tabs.store.api.models.response import CatalogOfferModel
from rare.models.image import ImageSize
from rare.utils.misc import qta_icon
from rare.utils.qt_requests import QtRequests
from .image import LoadingImageWidget
logger = logging.getLogger("StoreWidgets")
class ItemWidget(LoadingImageWidget):
show_details = pyqtSignal(CatalogOfferModel)
def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None):
super(ItemWidget, self).__init__(manager, parent=parent)
self.catalog_game = catalog_game
def mousePressEvent(self, a0: QMouseEvent) -> None:
if a0.button() == Qt.LeftButton:
a0.accept()
self.show_details.emit(self.catalog_game)
if a0.button() == Qt.RightButton:
a0.accept()
class StoreItemWidget(ItemWidget):
def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None):
super(StoreItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.DisplayWide)
self.ui.setupUi(self)
if catalog_game:
self.init_ui(catalog_game)
def init_ui(self, game: CatalogOfferModel):
if not game:
self.ui.title_label.setText(self.tr("An error occurred"))
return
self.ui.title_label.setText(game.title)
for attr in game.customAttributes:
if attr["key"] == "developerName":
developer = attr["value"]
break
else:
developer = game.seller["name"]
self.ui.developer_label.setText(developer)
price = game.price.totalPrice.fmtPrice["originalPrice"]
discount_price = game.price.totalPrice.fmtPrice["discountPrice"]
self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}')
if price != discount_price:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
key_images = game.keyImages
self.fetchPixmap(key_images.for_dimensions(self.width(), self.height()).url)
# for img in json_info["keyImages"]:
# if img["type"] in ["DieselStoreFrontWide", "OfferImageWide", "VaultClosed", "ProductLogo"]:
# if img["type"] == "VaultClosed" and json_info["title"] != "Mystery Game":
# continue
# self.fetchPixmap(img["url"])
# break
# else:
# logger.info(", ".join([img["type"] for img in json_info["keyImages"]]))
class ResultsItemWidget(ItemWidget):
def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None):
super(ResultsItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.Display)
self.ui.setupUi(self)
key_images = catalog_game.keyImages
self.fetchPixmap(key_images.for_dimensions(self.width(), self.height()).url)
self.ui.title_label.setText(catalog_game.title)
price = catalog_game.price.totalPrice.fmtPrice["originalPrice"]
discount_price = catalog_game.price.totalPrice.fmtPrice["discountPrice"]
self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}')
if price != discount_price:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
class WishlistItemWidget(ItemWidget):
delete_from_wishlist = pyqtSignal(CatalogOfferModel)
def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None):
super(WishlistItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.DisplayWide)
self.ui.setupUi(self)
for attr in catalog_game.customAttributes:
if attr["key"] == "developerName":
developer = attr["value"]
break
else:
developer = catalog_game.seller["name"]
original_price = catalog_game.price.totalPrice.fmtPrice["originalPrice"]
discount_price = catalog_game.price.totalPrice.fmtPrice["discountPrice"]
self.ui.title_label.setText(catalog_game.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:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
key_images = catalog_game.keyImages
self.fetchPixmap(
key_images.for_dimensions(self.width(), self.height()).url
)
self.delete_button = QPushButton(self)
self.delete_button.setIcon(qta_icon("mdi.delete", color="white"))
self.delete_button.clicked.connect(
lambda: self.delete_from_wishlist.emit(self.catalog_game)
)
self.layout().insertWidget(0, self.delete_button, alignment=Qt.AlignRight)

View file

@ -1,45 +1,116 @@
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QStackedWidget, QMessageBox
from enum import IntEnum
from typing import List
from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot
from PyQt5.QtGui import QShowEvent
from PyQt5.QtWidgets import QMessageBox, QWidget, QSizePolicy
from rare.components.tabs.store import ShopApiCore
from rare.components.tabs.store.game_widgets import WishlistWidget
from rare.ui.components.tabs.store.wishlist import Ui_Wishlist
from rare.utils.extra_widgets import WaitingSpinner
from rare.utils.misc import qta_icon as icon
from rare.utils.misc import qta_icon
from rare.widgets.flow_layout import FlowLayout
from rare.widgets.side_tab import SideTabContents
from rare.widgets.sliding_stack import SlidingStackedWidget
from .api.models.response import WishlistItemModel, CatalogOfferModel
from .store_api import StoreAPI
from .widgets.details import DetailsWidget
from .widgets.items import WishlistItemWidget
class Wishlist(QStackedWidget, Ui_Wishlist):
show_game_info = pyqtSignal(dict)
class WishlistPage(SlidingStackedWidget, SideTabContents):
def __init__(self, api: StoreAPI, parent=None):
super(WishlistPage, self).__init__(parent=parent)
self.implements_scrollarea = True
self.wishlist_widget = WishlistWidget(api, parent=self)
self.wishlist_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.wishlist_widget.set_title.connect(self.set_title)
self.wishlist_widget.show_details.connect(self.show_details)
self.details_widget = DetailsWidget([], api, parent=self)
self.details_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.details_widget.set_title.connect(self.set_title)
self.details_widget.back_clicked.connect(self.show_main)
self.setDirection(Qt.Horizontal)
self.addWidget(self.wishlist_widget)
self.addWidget(self.details_widget)
@pyqtSlot()
def show_main(self):
self.slideInWidget(self.wishlist_widget)
@pyqtSlot(object)
def show_details(self, game: CatalogOfferModel):
self.details_widget.update_game(game)
self.slideInWidget(self.details_widget)
class WishlistOrder(IntEnum):
NAME = 1
PRICE = 2
DISCOUNT = 3
DEVELOPER = 4
class WishlistFilter(IntEnum):
NONE = 0
DISCOUNT = 1
class WishlistWidget(QWidget, SideTabContents):
show_details = pyqtSignal(CatalogOfferModel)
update_wishlist_signal = pyqtSignal()
def __init__(self, api_core: ShopApiCore):
super(Wishlist, self).__init__()
self.api_core = api_core
self.setupUi(self)
self.addWidget(WaitingSpinner())
self.setCurrentIndex(1)
self.wishlist = []
self.widgets = []
def __init__(self, api: StoreAPI, parent=None):
super(WishlistWidget, self).__init__(parent=parent)
self.implements_scrollarea = True
self.api = api
self.ui = Ui_Wishlist()
self.ui.setupUi(self)
self.ui.main_layout.setContentsMargins(0, 0, 3, 0)
self.sort_cb.currentIndexChanged.connect(
lambda i: self.set_wishlist(self.wishlist, i)
)
self.filter_cb.currentIndexChanged.connect(self.set_filter)
self.reload_button.clicked.connect(self.update_wishlist)
self.reload_button.setIcon(icon("fa.refresh", color="white"))
self.wishlist_layout = FlowLayout()
self.ui.container_layout.addLayout(self.wishlist_layout, stretch=1)
self.reverse.stateChanged.connect(
lambda: self.set_wishlist(sort=self.sort_cb.currentIndex())
filters = {
WishlistFilter.NONE: self.tr("All items"),
WishlistFilter.DISCOUNT: self.tr("Discount"),
}
for data, text in filters.items():
self.ui.filter_combo.addItem(text, data)
self.ui.filter_combo.currentIndexChanged.connect(self.filter_wishlist)
sortings = {
WishlistOrder.NAME: self.tr("Name"),
WishlistOrder.PRICE: self.tr("Price"),
WishlistOrder.DISCOUNT: self.tr("Discount"),
WishlistOrder.DEVELOPER: self.tr("Developer"),
}
for data, text in sortings.items():
self.ui.order_combo.addItem(text, data)
self.ui.order_combo.currentIndexChanged.connect(self.order_wishlist)
self.ui.reload_button.setIcon(qta_icon("fa.refresh", color="white"))
self.ui.reload_button.clicked.connect(self.update_wishlist)
self.ui.reverse_check.stateChanged.connect(
lambda: self.order_wishlist(self.ui.order_combo.currentIndex())
)
self.setEnabled(False)
def showEvent(self, a0: QShowEvent) -> None:
self.update_wishlist()
return super().showEvent(a0)
def update_wishlist(self):
self.setCurrentIndex(1)
self.api_core.get_wishlist(self.set_wishlist)
self.setEnabled(False)
self.api.get_wishlist(self.set_wishlist)
def delete_from_wishlist(self, game):
self.api_core.remove_from_wishlist(
game["namespace"],
game["id"],
def delete_from_wishlist(self, game: CatalogOfferModel):
self.api.remove_from_wishlist(
game.namespace,
game.id,
lambda success: self.update_wishlist()
if success
else QMessageBox.warning(
@ -48,72 +119,68 @@ class Wishlist(QStackedWidget, Ui_Wishlist):
)
self.update_wishlist_signal.emit()
def set_filter(self, i):
count = 0
for w in self.widgets:
if i == 1 and not w.discount:
w.setVisible(False)
@pyqtSlot(int)
def filter_wishlist(self, index: int = int(WishlistFilter.NONE)):
list_filter = self.ui.filter_combo.itemData(index, Qt.UserRole)
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly)
for w in widgets:
if list_filter == WishlistFilter.NONE:
w.setVisible(True)
elif list_filter == WishlistFilter.DISCOUNT:
w.setVisible(bool(w.catalog_game.price.totalPrice.discount))
else:
w.setVisible(True)
count += 1
have_visible = any(map(lambda x: x.isVisible(), widgets))
self.ui.no_games_label.setVisible(not have_visible)
if i == 0:
w.setVisible(True)
@pyqtSlot(int)
def order_wishlist(self, index: int = int(WishlistOrder.NAME)):
list_order = self.ui.order_combo.itemData(index, Qt.UserRole)
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly)
for w in widgets:
self.wishlist_layout.removeWidget(w)
if count == 0:
self.no_games_label.setVisible(True)
if list_order == WishlistOrder.NAME:
def func(x: WishlistItemWidget):
return x.catalog_game.title
elif list_order == WishlistOrder.PRICE:
def func(x: WishlistItemWidget):
return x.catalog_game.price.totalPrice.discountPrice
elif list_order == WishlistOrder.DEVELOPER:
def func(x: WishlistItemWidget):
return x.catalog_game.seller["name"]
elif list_order == WishlistOrder.DISCOUNT:
def func(x: WishlistItemWidget):
discount = x.catalog_game.price.totalPrice.discountPrice
original = x.catalog_game.price.totalPrice.originalPrice
return 1 - (discount / original)
else:
self.no_games_label.setVisible(False)
def func(x: WishlistItemWidget):
return x.catalog_game.title
def set_wishlist(self, wishlist=None, sort=0):
reverse = self.ui.reverse_check.isChecked()
widgets = sorted(widgets, key=func, reverse=reverse)
for w in widgets:
self.wishlist_layout.addWidget(w)
def set_wishlist(self, wishlist: List[WishlistItemModel] = None):
if wishlist and wishlist[0] == "error":
return
if wishlist is not None:
self.wishlist = wishlist
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly)
for w in widgets:
self.wishlist_layout.removeWidget(w)
w.deleteLater()
for i in self.widgets:
i.deleteLater()
self.ui.no_games_label.setVisible(bool(wishlist))
if sort == 0:
sorted_list = sorted(self.wishlist, key=lambda x: x["offer"]["title"])
elif sort == 1:
sorted_list = sorted(
self.wishlist,
key=lambda x: x["offer"]["price"]["totalPrice"]["fmtPrice"][
"discountPrice"
],
)
elif sort == 2:
sorted_list = sorted(
self.wishlist, key=lambda x: x["offer"]["seller"]["name"]
)
elif sort == 3:
sorted_list = sorted(
self.wishlist,
reverse=True,
key=lambda x: 1
- (
x["offer"]["price"]["totalPrice"]["discountPrice"]
/ x["offer"]["price"]["totalPrice"]["originalPrice"]
),
)
else:
sorted_list = self.wishlist
self.widgets.clear()
if len(sorted_list) == 0:
self.no_games_label.setVisible(True)
else:
self.no_games_label.setVisible(False)
if self.reverse.isChecked():
sorted_list.reverse()
for game in sorted_list:
w = WishlistWidget(game["offer"])
self.widgets.append(w)
self.list_layout.addWidget(w)
w.open_game.connect(self.show_game_info.emit)
for game in wishlist:
w = WishlistItemWidget(self.api.cached_manager, game.offer, self.ui.container)
w.show_details.connect(self.show_details)
w.delete_from_wishlist.connect(self.delete_from_wishlist)
self.setCurrentIndex(0)
self.wishlist_layout.addWidget(w)
self.order_wishlist(self.ui.order_combo.currentIndex())
self.filter_wishlist(self.ui.filter_combo.currentIndex())
self.setEnabled(True)

Binary file not shown.

View file

@ -64,6 +64,14 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
css = qstylizer.style.StyleSheet()
# Generic flat button
css['QPushButton[flat="true"]'].setValues(
border="0px",
borderRadius="5px",
backgroundColor="rgba(255, 255, 255, 5%)",
)
# InfoLabel
css.QLabel["#InfoLabel"].setValues(
color="#999",

View file

@ -1,6 +1,11 @@
/* This file is auto-generated from "stylesheet.py". DO NOT EDIT!!! */
QPushButton[flat="true"] {
border: 0px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 5%);
}
QLabel#InfoLabel {
color: #999;
font-style: italic;

View file

@ -0,0 +1,216 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/details.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_DetailsWidget(object):
def setupUi(self, DetailsWidget):
DetailsWidget.setObjectName("DetailsWidget")
DetailsWidget.resize(630, 371)
DetailsWidget.setWindowTitle("DetailsWidget")
self.main_layout = QtWidgets.QHBoxLayout(DetailsWidget)
self.main_layout.setObjectName("main_layout")
self.left_layout = QtWidgets.QVBoxLayout()
self.left_layout.setObjectName("left_layout")
self.back_button = QtWidgets.QPushButton(DetailsWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.back_button.sizePolicy().hasHeightForWidth())
self.back_button.setSizePolicy(sizePolicy)
self.back_button.setText("")
self.back_button.setIconSize(QtCore.QSize(32, 32))
self.back_button.setFlat(True)
self.back_button.setObjectName("back_button")
self.left_layout.addWidget(self.back_button)
self.main_layout.addLayout(self.left_layout)
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setObjectName("right_layout")
self.details_layout = QtWidgets.QFormLayout()
self.details_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.details_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.FieldsStayAtSizeHint)
self.details_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.details_layout.setContentsMargins(6, 6, 6, 6)
self.details_layout.setSpacing(12)
self.details_layout.setObjectName("details_layout")
self.title_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.title_label.setObjectName("title_label")
self.details_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.title_label)
self.title = QtWidgets.QLabel(DetailsWidget)
self.title.setText("title")
self.title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.title.setObjectName("title")
self.details_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.title)
self.developer_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.developer_label.setFont(font)
self.developer_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.developer_label.setObjectName("developer_label")
self.details_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.developer_label)
self.developer = QtWidgets.QLabel(DetailsWidget)
self.developer.setText("developer")
self.developer.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.developer.setObjectName("developer")
self.details_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.developer)
self.publisher_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.publisher_label.setFont(font)
self.publisher_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.publisher_label.setObjectName("publisher_label")
self.details_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.publisher_label)
self.publisher = QtWidgets.QLabel(DetailsWidget)
self.publisher.setText("publisher")
self.publisher.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.publisher.setObjectName("publisher")
self.details_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.publisher)
self.status_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.status_label.setFont(font)
self.status_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.status_label.setObjectName("status_label")
self.details_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.status_label)
self.status = QtWidgets.QLabel(DetailsWidget)
self.status.setObjectName("status")
self.details_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.status)
self.price_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.price_label.setFont(font)
self.price_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.price_label.setObjectName("price_label")
self.details_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.price_label)
self.tags_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.tags_label.setFont(font)
self.tags_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.tags_label.setObjectName("tags_label")
self.details_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.tags_label)
self.tags = QtWidgets.QLabel(DetailsWidget)
self.tags.setText("tags")
self.tags.setObjectName("tags")
self.details_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.tags)
self.social_links_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.social_links_label.setFont(font)
self.social_links_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.social_links_label.setObjectName("social_links_label")
self.details_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.social_links_label)
self.actions_label = QtWidgets.QLabel(DetailsWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.actions_label.setFont(font)
self.actions_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.actions_label.setObjectName("actions_label")
self.details_layout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.actions_label)
self.social_links = QtWidgets.QWidget(DetailsWidget)
self.social_links.setObjectName("social_links")
self.social_links_layout = QtWidgets.QHBoxLayout(self.social_links)
self.social_links_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.social_links_layout.setContentsMargins(0, 0, 0, 0)
self.social_links_layout.setObjectName("social_links_layout")
self.details_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.social_links)
self.actions = QtWidgets.QWidget(DetailsWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.actions.sizePolicy().hasHeightForWidth())
self.actions.setSizePolicy(sizePolicy)
self.actions.setMinimumSize(QtCore.QSize(250, 0))
self.actions.setObjectName("actions")
self.actions_layout = QtWidgets.QVBoxLayout(self.actions)
self.actions_layout.setContentsMargins(0, 0, 0, 0)
self.actions_layout.setObjectName("actions_layout")
self.store_button = QtWidgets.QPushButton(self.actions)
self.store_button.setObjectName("store_button")
self.actions_layout.addWidget(self.store_button)
self.wishlist_button = QtWidgets.QPushButton(self.actions)
self.wishlist_button.setObjectName("wishlist_button")
self.actions_layout.addWidget(self.wishlist_button)
self.details_layout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.actions)
self.price = QtWidgets.QWidget(DetailsWidget)
self.price.setObjectName("price")
self.price_layout = QtWidgets.QHBoxLayout(self.price)
self.price_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.price_layout.setContentsMargins(0, 0, 0, 0)
self.price_layout.setObjectName("price_layout")
self.original_price = QtWidgets.QLabel(self.price)
self.original_price.setText("orignal")
self.original_price.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.original_price.setObjectName("original_price")
self.price_layout.addWidget(self.original_price)
self.discount_price = QtWidgets.QLabel(self.price)
self.discount_price.setText("discount")
self.discount_price.setObjectName("discount_price")
self.price_layout.addWidget(self.discount_price)
self.details_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.price)
self.right_layout.addLayout(self.details_layout)
self.requirements_frame = QtWidgets.QFrame(DetailsWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.requirements_frame.sizePolicy().hasHeightForWidth())
self.requirements_frame.setSizePolicy(sizePolicy)
self.requirements_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.requirements_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
self.requirements_frame.setObjectName("requirements_frame")
self.requirements_layout = QtWidgets.QHBoxLayout(self.requirements_frame)
self.requirements_layout.setContentsMargins(0, 0, 0, 0)
self.requirements_layout.setObjectName("requirements_layout")
self.right_layout.addWidget(self.requirements_frame)
self.description_label = QtWidgets.QTextBrowser(DetailsWidget)
self.description_label.setOpenExternalLinks(True)
self.description_label.setObjectName("description_label")
self.right_layout.addWidget(self.description_label)
self.main_layout.addLayout(self.right_layout)
self.main_layout.setStretch(1, 1)
self.retranslateUi(DetailsWidget)
def retranslateUi(self, DetailsWidget):
_translate = QtCore.QCoreApplication.translate
self.title_label.setText(_translate("DetailsWidget", "Title"))
self.developer_label.setText(_translate("DetailsWidget", "Developer"))
self.publisher_label.setText(_translate("DetailsWidget", "Publisher"))
self.status_label.setText(_translate("DetailsWidget", "Status"))
self.status.setText(_translate("DetailsWidget", "You already own this game"))
self.price_label.setText(_translate("DetailsWidget", "Price"))
self.tags_label.setText(_translate("DetailsWidget", "Tags"))
self.social_links_label.setText(_translate("DetailsWidget", "Links"))
self.actions_label.setText(_translate("DetailsWidget", "Actions"))
self.store_button.setText(_translate("DetailsWidget", "Buy in Epic Games Store"))
self.wishlist_button.setText(_translate("DetailsWidget", "Add to wishlist"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
DetailsWidget = QtWidgets.QWidget()
ui = Ui_DetailsWidget()
ui.setupUi(DetailsWidget)
DetailsWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,394 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DetailsWidget</class>
<widget class="QWidget" name="DetailsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>630</width>
<height>371</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">DetailsWidget</string>
</property>
<layout class="QHBoxLayout" name="main_layout" stretch="0,1">
<item>
<layout class="QVBoxLayout" name="left_layout">
<item>
<widget class="QPushButton" name="back_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="right_layout">
<item>
<layout class="QFormLayout" name="details_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Title</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="title">
<property name="text">
<string notr="true">title</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="developer_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Developer</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="developer">
<property name="text">
<string notr="true">developer</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="publisher_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Publisher</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="publisher">
<property name="text">
<string notr="true">publisher</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="status_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Status</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="status">
<property name="text">
<string>You already own this game</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="price_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Price</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="tags_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Tags</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="tags">
<property name="text">
<string notr="true">tags</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="social_links_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Links</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="actions_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Actions</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QWidget" name="social_links" native="true">
<layout class="QHBoxLayout" name="social_links_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QWidget" name="actions" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="actions_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="store_button">
<property name="text">
<string>Buy in Epic Games Store</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="wishlist_button">
<property name="text">
<string>Add to wishlist</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<widget class="QWidget" name="price" native="true">
<layout class="QHBoxLayout" name="price_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="original_price">
<property name="text">
<string notr="true">orignal</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="discount_price">
<property name="text">
<string notr="true">discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="requirements_frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="requirements_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="description_label">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/landing.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LandingWidget(object):
def setupUi(self, LandingWidget):
LandingWidget.setObjectName("LandingWidget")
LandingWidget.resize(788, 662)
LandingWidget.setWindowTitle("LandingWidget")
self.main_layout = QtWidgets.QHBoxLayout(LandingWidget)
self.main_layout.setObjectName("main_layout")
self.left_layout = QtWidgets.QVBoxLayout()
self.left_layout.setObjectName("left_layout")
self.main_layout.addLayout(self.left_layout)
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setObjectName("right_layout")
self.reset_button = QtWidgets.QPushButton(LandingWidget)
self.reset_button.setObjectName("reset_button")
self.right_layout.addWidget(self.reset_button)
self.filter_scrollarea = QtWidgets.QScrollArea(LandingWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_scrollarea.sizePolicy().hasHeightForWidth())
self.filter_scrollarea.setSizePolicy(sizePolicy)
self.filter_scrollarea.setFrameShape(QtWidgets.QFrame.NoFrame)
self.filter_scrollarea.setFrameShadow(QtWidgets.QFrame.Plain)
self.filter_scrollarea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.filter_scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.filter_scrollarea.setWidgetResizable(True)
self.filter_scrollarea.setObjectName("filter_scrollarea")
self.filter_container = QtWidgets.QWidget()
self.filter_container.setGeometry(QtCore.QRect(0, 0, 142, 390))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_container.sizePolicy().hasHeightForWidth())
self.filter_container.setSizePolicy(sizePolicy)
self.filter_container.setObjectName("filter_container")
self.filter_container_layout = QtWidgets.QVBoxLayout(self.filter_container)
self.filter_container_layout.setContentsMargins(0, 0, 3, 0)
self.filter_container_layout.setObjectName("filter_container_layout")
self.price_group = QtWidgets.QGroupBox(self.filter_container)
self.price_group.setObjectName("price_group")
self.price_layout = QtWidgets.QVBoxLayout(self.price_group)
self.price_layout.setObjectName("price_layout")
self.none_price = QtWidgets.QRadioButton(self.price_group)
self.none_price.setChecked(True)
self.none_price.setObjectName("none_price")
self.price_layout.addWidget(self.none_price)
self.free_button = QtWidgets.QRadioButton(self.price_group)
self.free_button.setObjectName("free_button")
self.price_layout.addWidget(self.free_button)
self.under10 = QtWidgets.QRadioButton(self.price_group)
self.under10.setObjectName("under10")
self.price_layout.addWidget(self.under10)
self.under20 = QtWidgets.QRadioButton(self.price_group)
self.under20.setObjectName("under20")
self.price_layout.addWidget(self.under20)
self.under30 = QtWidgets.QRadioButton(self.price_group)
self.under30.setObjectName("under30")
self.price_layout.addWidget(self.under30)
self.above = QtWidgets.QRadioButton(self.price_group)
self.above.setObjectName("above")
self.price_layout.addWidget(self.above)
self.on_discount = QtWidgets.QCheckBox(self.price_group)
self.on_discount.setObjectName("on_discount")
self.price_layout.addWidget(self.on_discount)
self.filter_container_layout.addWidget(self.price_group)
self.platform_group = QtWidgets.QGroupBox(self.filter_container)
self.platform_group.setObjectName("platform_group")
self.platfrom_layout = QtWidgets.QVBoxLayout(self.platform_group)
self.platfrom_layout.setObjectName("platfrom_layout")
self.filter_container_layout.addWidget(self.platform_group)
self.genre_group = QtWidgets.QGroupBox(self.filter_container)
self.genre_group.setObjectName("genre_group")
self.genre_layout = QtWidgets.QVBoxLayout(self.genre_group)
self.genre_layout.setObjectName("genre_layout")
self.filter_container_layout.addWidget(self.genre_group)
self.type_group = QtWidgets.QGroupBox(self.filter_container)
self.type_group.setObjectName("type_group")
self.type_layout = QtWidgets.QVBoxLayout(self.type_group)
self.type_layout.setObjectName("type_layout")
self.filter_container_layout.addWidget(self.type_group)
self.others_group = QtWidgets.QGroupBox(self.filter_container)
self.others_group.setObjectName("others_group")
self.others_layout = QtWidgets.QVBoxLayout(self.others_group)
self.others_layout.setObjectName("others_layout")
self.filter_container_layout.addWidget(self.others_group)
self.filter_scrollarea.setWidget(self.filter_container)
self.right_layout.addWidget(self.filter_scrollarea)
self.main_layout.addLayout(self.right_layout)
self.main_layout.setStretch(0, 1)
self.retranslateUi(LandingWidget)
def retranslateUi(self, LandingWidget):
_translate = QtCore.QCoreApplication.translate
self.reset_button.setText(_translate("LandingWidget", "Reset filters"))
self.price_group.setTitle(_translate("LandingWidget", "Price"))
self.none_price.setText(_translate("LandingWidget", "None"))
self.free_button.setText(_translate("LandingWidget", "Free"))
self.under10.setText(_translate("LandingWidget", "Under 10"))
self.under20.setText(_translate("LandingWidget", "Under 20"))
self.under30.setText(_translate("LandingWidget", "Under 30"))
self.above.setText(_translate("LandingWidget", "14.99 and above"))
self.on_discount.setText(_translate("LandingWidget", "Discount"))
self.platform_group.setTitle(_translate("LandingWidget", "Platform"))
self.genre_group.setTitle(_translate("LandingWidget", "Genre"))
self.type_group.setTitle(_translate("LandingWidget", "Type"))
self.others_group.setTitle(_translate("LandingWidget", "Other tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
LandingWidget = QtWidgets.QWidget()
ui = Ui_LandingWidget()
ui.setupUi(LandingWidget)
LandingWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LandingWidget</class>
<widget class="QWidget" name="LandingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>788</width>
<height>662</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">LandingWidget</string>
</property>
<layout class="QHBoxLayout" name="main_layout" stretch="1,0">
<item>
<layout class="QVBoxLayout" name="left_layout"/>
</item>
<item>
<layout class="QVBoxLayout" name="right_layout">
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset filters</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="filter_scrollarea">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="filter_container">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>142</width>
<height>390</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="filter_container_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="price_group">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="price_layout">
<item>
<widget class="QRadioButton" name="none_price">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="free_button">
<property name="text">
<string>Free</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under10">
<property name="text">
<string>Under 10</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under20">
<property name="text">
<string>Under 20</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under30">
<property name="text">
<string>Under 30</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="above">
<property name="text">
<string>14.99 and above</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_group">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="platfrom_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_group">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="genre_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_group">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="type_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_group">
<property name="title">
<string>Other tags</string>
</property>
<layout class="QVBoxLayout" name="others_layout"/>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/search.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_SearchWidget(object):
def setupUi(self, SearchWidget):
SearchWidget.setObjectName("SearchWidget")
SearchWidget.resize(491, 382)
SearchWidget.setWindowTitle("SearchWidget")
self.main_layout = QtWidgets.QHBoxLayout(SearchWidget)
self.main_layout.setObjectName("main_layout")
self.left_layout = QtWidgets.QVBoxLayout()
self.left_layout.setObjectName("left_layout")
self.main_layout.addLayout(self.left_layout)
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setObjectName("right_layout")
self.reset_button = QtWidgets.QPushButton(SearchWidget)
self.reset_button.setObjectName("reset_button")
self.right_layout.addWidget(self.reset_button)
self.filter_scrollarea = QtWidgets.QScrollArea(SearchWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_scrollarea.sizePolicy().hasHeightForWidth())
self.filter_scrollarea.setSizePolicy(sizePolicy)
self.filter_scrollarea.setFrameShape(QtWidgets.QFrame.NoFrame)
self.filter_scrollarea.setFrameShadow(QtWidgets.QFrame.Plain)
self.filter_scrollarea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.filter_scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.filter_scrollarea.setWidgetResizable(True)
self.filter_scrollarea.setObjectName("filter_scrollarea")
self.filter_container = QtWidgets.QWidget()
self.filter_container.setGeometry(QtCore.QRect(0, 0, 142, 390))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_container.sizePolicy().hasHeightForWidth())
self.filter_container.setSizePolicy(sizePolicy)
self.filter_container.setObjectName("filter_container")
self.filter_container_layout = QtWidgets.QVBoxLayout(self.filter_container)
self.filter_container_layout.setContentsMargins(0, 0, 3, 0)
self.filter_container_layout.setObjectName("filter_container_layout")
self.price_group = QtWidgets.QGroupBox(self.filter_container)
self.price_group.setObjectName("price_group")
self.price_layout = QtWidgets.QVBoxLayout(self.price_group)
self.price_layout.setObjectName("price_layout")
self.none_price = QtWidgets.QRadioButton(self.price_group)
self.none_price.setChecked(True)
self.none_price.setObjectName("none_price")
self.price_layout.addWidget(self.none_price)
self.free_button = QtWidgets.QRadioButton(self.price_group)
self.free_button.setObjectName("free_button")
self.price_layout.addWidget(self.free_button)
self.under10 = QtWidgets.QRadioButton(self.price_group)
self.under10.setObjectName("under10")
self.price_layout.addWidget(self.under10)
self.under20 = QtWidgets.QRadioButton(self.price_group)
self.under20.setObjectName("under20")
self.price_layout.addWidget(self.under20)
self.under30 = QtWidgets.QRadioButton(self.price_group)
self.under30.setObjectName("under30")
self.price_layout.addWidget(self.under30)
self.above = QtWidgets.QRadioButton(self.price_group)
self.above.setObjectName("above")
self.price_layout.addWidget(self.above)
self.on_discount = QtWidgets.QCheckBox(self.price_group)
self.on_discount.setObjectName("on_discount")
self.price_layout.addWidget(self.on_discount)
self.filter_container_layout.addWidget(self.price_group)
self.platform_group = QtWidgets.QGroupBox(self.filter_container)
self.platform_group.setObjectName("platform_group")
self.platfrom_layout = QtWidgets.QVBoxLayout(self.platform_group)
self.platfrom_layout.setObjectName("platfrom_layout")
self.filter_container_layout.addWidget(self.platform_group)
self.genre_group = QtWidgets.QGroupBox(self.filter_container)
self.genre_group.setObjectName("genre_group")
self.genre_layout = QtWidgets.QVBoxLayout(self.genre_group)
self.genre_layout.setObjectName("genre_layout")
self.filter_container_layout.addWidget(self.genre_group)
self.type_group = QtWidgets.QGroupBox(self.filter_container)
self.type_group.setObjectName("type_group")
self.type_layout = QtWidgets.QVBoxLayout(self.type_group)
self.type_layout.setObjectName("type_layout")
self.filter_container_layout.addWidget(self.type_group)
self.others_group = QtWidgets.QGroupBox(self.filter_container)
self.others_group.setObjectName("others_group")
self.others_layout = QtWidgets.QVBoxLayout(self.others_group)
self.others_layout.setObjectName("others_layout")
self.filter_container_layout.addWidget(self.others_group)
self.filter_scrollarea.setWidget(self.filter_container)
self.right_layout.addWidget(self.filter_scrollarea)
self.main_layout.addLayout(self.right_layout)
self.main_layout.setStretch(0, 1)
self.retranslateUi(SearchWidget)
def retranslateUi(self, SearchWidget):
_translate = QtCore.QCoreApplication.translate
self.reset_button.setText(_translate("SearchWidget", "Reset filters"))
self.price_group.setTitle(_translate("SearchWidget", "Price"))
self.none_price.setText(_translate("SearchWidget", "None"))
self.free_button.setText(_translate("SearchWidget", "Free"))
self.under10.setText(_translate("SearchWidget", "Under 10"))
self.under20.setText(_translate("SearchWidget", "Under 20"))
self.under30.setText(_translate("SearchWidget", "Under 30"))
self.above.setText(_translate("SearchWidget", "14.99 and above"))
self.on_discount.setText(_translate("SearchWidget", "Discount"))
self.platform_group.setTitle(_translate("SearchWidget", "Platform"))
self.genre_group.setTitle(_translate("SearchWidget", "Genre"))
self.type_group.setTitle(_translate("SearchWidget", "Type"))
self.others_group.setTitle(_translate("SearchWidget", "Other tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
SearchWidget = QtWidgets.QWidget()
ui = Ui_SearchWidget()
ui.setupUi(SearchWidget)
SearchWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchWidget</class>
<widget class="QWidget" name="SearchWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>491</width>
<height>382</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">SearchWidget</string>
</property>
<layout class="QHBoxLayout" name="main_layout" stretch="1,0">
<item>
<layout class="QVBoxLayout" name="left_layout"/>
</item>
<item>
<layout class="QVBoxLayout" name="right_layout">
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset filters</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="filter_scrollarea">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="filter_container">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>142</width>
<height>390</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="filter_container_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="price_group">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="price_layout">
<item>
<widget class="QRadioButton" name="none_price">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="free_button">
<property name="text">
<string>Free</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under10">
<property name="text">
<string>Under 10</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under20">
<property name="text">
<string>Under 20</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under30">
<property name="text">
<string>Under 30</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="above">
<property name="text">
<string>14.99 and above</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_group">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="platfrom_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_group">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="genre_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_group">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="type_layout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_group">
<property name="title">
<string>Other tags</string>
</property>
<layout class="QVBoxLayout" name="others_layout"/>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/shop_game_info.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_shop_info(object):
def setupUi(self, shop_info):
shop_info.setObjectName("shop_info")
shop_info.resize(702, 468)
shop_info.setWindowTitle("Form")
self.verticalLayout = QtWidgets.QVBoxLayout(shop_info)
self.verticalLayout.setObjectName("verticalLayout")
self.back_button = QtWidgets.QPushButton(shop_info)
self.back_button.setObjectName("back_button")
self.verticalLayout.addWidget(self.back_button)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.image_stack = QtWidgets.QStackedWidget(shop_info)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.image_stack.sizePolicy().hasHeightForWidth())
self.image_stack.setSizePolicy(sizePolicy)
self.image_stack.setObjectName("image_stack")
self.horizontalLayout.addWidget(self.image_stack)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title = QtWidgets.QLabel(shop_info)
font = QtGui.QFont()
font.setPointSize(18)
self.title.setFont(font)
self.title.setText("")
self.title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.title.setObjectName("title")
self.verticalLayout_2.addWidget(self.title)
self.dev = QtWidgets.QLabel(shop_info)
font = QtGui.QFont()
font.setPointSize(14)
self.dev.setFont(font)
self.dev.setText("")
self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.dev.setObjectName("dev")
self.verticalLayout_2.addWidget(self.dev)
self.owned_label = QtWidgets.QLabel(shop_info)
self.owned_label.setObjectName("owned_label")
self.verticalLayout_2.addWidget(self.owned_label)
self.price = QtWidgets.QLabel(shop_info)
self.price.setText("")
self.price.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.price.setObjectName("price")
self.verticalLayout_2.addWidget(self.price)
self.discount_price = QtWidgets.QLabel(shop_info)
self.discount_price.setText("")
self.discount_price.setObjectName("discount_price")
self.verticalLayout_2.addWidget(self.discount_price)
self.tags = QtWidgets.QLabel(shop_info)
self.tags.setText("")
self.tags.setObjectName("tags")
self.verticalLayout_2.addWidget(self.tags)
self.open_store_button = QtWidgets.QPushButton(shop_info)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.open_store_button.sizePolicy().hasHeightForWidth())
self.open_store_button.setSizePolicy(sizePolicy)
self.open_store_button.setObjectName("open_store_button")
self.verticalLayout_2.addWidget(self.open_store_button)
self.wishlist_button = QtWidgets.QPushButton(shop_info)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.wishlist_button.sizePolicy().hasHeightForWidth())
self.wishlist_button.setSizePolicy(sizePolicy)
self.wishlist_button.setObjectName("wishlist_button")
self.verticalLayout_2.addWidget(self.wishlist_button)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout.addLayout(self.verticalLayout_2)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.req_group_box = QtWidgets.QGroupBox(shop_info)
self.req_group_box.setObjectName("req_group_box")
self.gridLayout_2 = QtWidgets.QGridLayout(self.req_group_box)
self.gridLayout_2.setObjectName("gridLayout_2")
self.verticalLayout.addWidget(self.req_group_box)
self.social_link_gb = QtWidgets.QGroupBox(shop_info)
self.social_link_gb.setObjectName("social_link_gb")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.social_link_gb)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout.addWidget(self.social_link_gb)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem2)
self.retranslateUi(shop_info)
self.image_stack.setCurrentIndex(-1)
def retranslateUi(self, shop_info):
_translate = QtCore.QCoreApplication.translate
self.back_button.setText(_translate("shop_info", "Back"))
self.owned_label.setText(_translate("shop_info", "You already own this game"))
self.open_store_button.setText(_translate("shop_info", "Buy Game in Epic Games Store"))
self.wishlist_button.setText(_translate("shop_info", "Add to wishlist"))
self.req_group_box.setTitle(_translate("shop_info", "Requirements"))
self.social_link_gb.setTitle(_translate("shop_info", "Social Links"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
shop_info = QtWidgets.QWidget()
ui = Ui_shop_info()
ui.setupUi(shop_info)
shop_info.show()
sys.exit(app.exec_())

View file

@ -1,191 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>shop_info</class>
<widget class="QWidget" name="shop_info">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>702</width>
<height>468</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="back_button">
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QStackedWidget" name="image_stack">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title">
<property name="font">
<font>
<pointsize>18</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dev">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="owned_label">
<property name="text">
<string>You already own this game</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="price">
<property name="text">
<string notr="true"/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="discount_price">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tags">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open_store_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Buy Game in Epic Games Store</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="wishlist_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add to wishlist</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="req_group_box">
<property name="title">
<string>Requirements</string>
</property>
<layout class="QGridLayout" name="gridLayout_2"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="social_link_gb">
<property name="title">
<string>Social Links</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,172 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/store.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtWidgets
class Ui_ShopWidget(object):
def setupUi(self, ShopWidget):
ShopWidget.setObjectName("ShopWidget")
ShopWidget.resize(850, 572)
ShopWidget.setWindowTitle("Form")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(ShopWidget)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.scrollArea = QtWidgets.QScrollArea(ShopWidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 828, 550))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.scrollAreaWidgetContents)
self.horizontalLayout.setObjectName("horizontalLayout")
self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget.setObjectName("widget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout.setObjectName("verticalLayout")
self.free_game_group_box = QtWidgets.QGroupBox(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.free_game_group_box.sizePolicy().hasHeightForWidth())
self.free_game_group_box.setSizePolicy(sizePolicy)
self.free_game_group_box.setObjectName("free_game_group_box")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.free_game_group_box)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.free_stack = QtWidgets.QStackedWidget(self.free_game_group_box)
self.free_stack.setObjectName("free_stack")
self.free_widget = QtWidgets.QWidget()
self.free_widget.setObjectName("free_widget")
self.free_stack.addWidget(self.free_widget)
self.verticalLayout_3.addWidget(self.free_stack)
self.verticalLayout.addWidget(self.free_game_group_box)
self.discounts_gb = QtWidgets.QGroupBox(self.widget)
self.discounts_gb.setObjectName("discounts_gb")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.discounts_gb)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.discount_stack = QtWidgets.QStackedWidget(self.discounts_gb)
self.discount_stack.setObjectName("discount_stack")
self.discount_widget = QtWidgets.QWidget()
self.discount_widget.setObjectName("discount_widget")
self.discount_stack.addWidget(self.discount_widget)
self.verticalLayout_6.addWidget(self.discount_stack)
self.verticalLayout.addWidget(self.discounts_gb)
self.filter_game_gb = QtWidgets.QGroupBox(self.widget)
self.filter_game_gb.setObjectName("filter_game_gb")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.filter_game_gb)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.game_stack = QtWidgets.QStackedWidget(self.filter_game_gb)
self.game_stack.setObjectName("game_stack")
self.game_widget = QtWidgets.QWidget()
self.game_widget.setObjectName("game_widget")
self.game_stack.addWidget(self.game_widget)
self.verticalLayout_4.addWidget(self.game_stack)
self.verticalLayout.addWidget(self.filter_game_gb)
self.horizontalLayout.addWidget(self.widget)
self.filter_gb = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_gb.sizePolicy().hasHeightForWidth())
self.filter_gb.setSizePolicy(sizePolicy)
self.filter_gb.setMinimumSize(QtCore.QSize(150, 0))
self.filter_gb.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.filter_gb.setObjectName("filter_gb")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.filter_gb)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.reset_button = QtWidgets.QPushButton(self.filter_gb)
self.reset_button.setObjectName("reset_button")
self.verticalLayout_2.addWidget(self.reset_button)
self.price_gb = QtWidgets.QGroupBox(self.filter_gb)
self.price_gb.setObjectName("price_gb")
self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.price_gb)
self.verticalLayout_9.setObjectName("verticalLayout_9")
self.none_price = QtWidgets.QRadioButton(self.price_gb)
self.none_price.setChecked(True)
self.none_price.setObjectName("none_price")
self.verticalLayout_9.addWidget(self.none_price)
self.free_button = QtWidgets.QRadioButton(self.price_gb)
self.free_button.setObjectName("free_button")
self.verticalLayout_9.addWidget(self.free_button)
self.under10 = QtWidgets.QRadioButton(self.price_gb)
self.under10.setObjectName("under10")
self.verticalLayout_9.addWidget(self.under10)
self.under20 = QtWidgets.QRadioButton(self.price_gb)
self.under20.setObjectName("under20")
self.verticalLayout_9.addWidget(self.under20)
self.under30 = QtWidgets.QRadioButton(self.price_gb)
self.under30.setObjectName("under30")
self.verticalLayout_9.addWidget(self.under30)
self.above = QtWidgets.QRadioButton(self.price_gb)
self.above.setObjectName("above")
self.verticalLayout_9.addWidget(self.above)
self.on_discount = QtWidgets.QCheckBox(self.price_gb)
self.on_discount.setObjectName("on_discount")
self.verticalLayout_9.addWidget(self.on_discount)
self.verticalLayout_2.addWidget(self.price_gb)
self.platform_gb = QtWidgets.QGroupBox(self.filter_gb)
self.platform_gb.setObjectName("platform_gb")
self.verticalLayout_13 = QtWidgets.QVBoxLayout(self.platform_gb)
self.verticalLayout_13.setObjectName("verticalLayout_13")
self.verticalLayout_2.addWidget(self.platform_gb)
self.genre_gb = QtWidgets.QGroupBox(self.filter_gb)
self.genre_gb.setObjectName("genre_gb")
self.verticalLayout_12 = QtWidgets.QVBoxLayout(self.genre_gb)
self.verticalLayout_12.setObjectName("verticalLayout_12")
self.verticalLayout_2.addWidget(self.genre_gb)
self.type_gb = QtWidgets.QGroupBox(self.filter_gb)
self.type_gb.setObjectName("type_gb")
self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.type_gb)
self.verticalLayout_11.setObjectName("verticalLayout_11")
self.verticalLayout_2.addWidget(self.type_gb)
self.others_gb = QtWidgets.QGroupBox(self.filter_gb)
self.others_gb.setObjectName("others_gb")
self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.others_gb)
self.verticalLayout_10.setObjectName("verticalLayout_10")
self.verticalLayout_2.addWidget(self.others_gb)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout.addWidget(self.filter_gb)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.horizontalLayout_2.addWidget(self.scrollArea)
self.verticalLayout_7.addLayout(self.horizontalLayout_2)
self.retranslateUi(ShopWidget)
def retranslateUi(self, ShopWidget):
_translate = QtCore.QCoreApplication.translate
self.free_game_group_box.setTitle(_translate("ShopWidget", "Free Games"))
self.discounts_gb.setTitle(_translate("ShopWidget", "Discounts from your wishlist"))
self.filter_game_gb.setTitle(_translate("ShopWidget", "Games"))
self.filter_gb.setTitle(_translate("ShopWidget", "Filter"))
self.reset_button.setText(_translate("ShopWidget", "Reset"))
self.price_gb.setTitle(_translate("ShopWidget", "Price"))
self.none_price.setText(_translate("ShopWidget", "None"))
self.free_button.setText(_translate("ShopWidget", "Free"))
self.under10.setText(_translate("ShopWidget", "Under 10"))
self.under20.setText(_translate("ShopWidget", "Under 20"))
self.under30.setText(_translate("ShopWidget", "Under 30"))
self.above.setText(_translate("ShopWidget", "14.99 and above"))
self.on_discount.setText(_translate("ShopWidget", "Discount"))
self.platform_gb.setTitle(_translate("ShopWidget", "Platform"))
self.genre_gb.setTitle(_translate("ShopWidget", "Genre"))
self.type_gb.setTitle(_translate("ShopWidget", "Type"))
self.others_gb.setTitle(_translate("ShopWidget", "Other Tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ShopWidget = QtWidgets.QWidget()
ui = Ui_ShopWidget()
ui.setupUi(ShopWidget)
ShopWidget.show()
sys.exit(app.exec_())

View file

@ -1,238 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShopWidget</class>
<widget class="QWidget" name="ShopWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>850</width>
<height>572</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>828</width>
<height>550</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="free_game_group_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Free Games</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QStackedWidget" name="free_stack">
<widget class="QWidget" name="free_widget"/>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="discounts_gb">
<property name="title">
<string>Discounts from your wishlist</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QStackedWidget" name="discount_stack">
<widget class="QWidget" name="discount_widget"/>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="filter_game_gb">
<property name="title">
<string>Games</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QStackedWidget" name="game_stack">
<widget class="QWidget" name="game_widget"/>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="filter_gb">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Filter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="price_gb">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QRadioButton" name="none_price">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="free_button">
<property name="text">
<string>Free</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under10">
<property name="text">
<string>Under 10</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under20">
<property name="text">
<string>Under 20</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under30">
<property name="text">
<string>Under 30</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="above">
<property name="text">
<string>14.99 and above</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_gb">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_gb">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_gb">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_gb">
<property name="title">
<string>Other Tags</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/wishlist_widget.ui'
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/widgets/wishlist_widget.ui'
#
# Created by: PyQt5 UI code generator 5.15.5
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@ -14,20 +14,26 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_WishlistWidget(object):
def setupUi(self, WishlistWidget):
WishlistWidget.setObjectName("WishlistWidget")
WishlistWidget.resize(523, 172)
WishlistWidget.setWindowTitle("Form")
self.horizontalLayout = QtWidgets.QHBoxLayout(WishlistWidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.widget = QtWidgets.QWidget(WishlistWidget)
WishlistWidget.resize(202, 94)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(WishlistWidget.sizePolicy().hasHeightForWidth())
WishlistWidget.setSizePolicy(sizePolicy)
WishlistWidget.setWindowTitle("WishlistWIdget")
self.main_layout = QtWidgets.QHBoxLayout(WishlistWidget)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setObjectName("main_layout")
self.info_widget = QtWidgets.QWidget(WishlistWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.widget.setSizePolicy(sizePolicy)
self.widget.setObjectName("widget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget)
sizePolicy.setHeightForWidth(self.info_widget.sizePolicy().hasHeightForWidth())
self.info_widget.setSizePolicy(sizePolicy)
self.info_widget.setObjectName("info_widget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.info_widget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title_label = QtWidgets.QLabel(self.widget)
self.title_label = QtWidgets.QLabel(self.info_widget)
font = QtGui.QFont()
font.setPointSize(16)
self.title_label.setFont(font)
@ -35,16 +41,16 @@ class Ui_WishlistWidget(object):
self.title_label.setWordWrap(True)
self.title_label.setObjectName("title_label")
self.verticalLayout_2.addWidget(self.title_label)
self.developer = QtWidgets.QLabel(self.widget)
self.developer = QtWidgets.QLabel(self.info_widget)
font = QtGui.QFont()
font.setPointSize(12)
self.developer.setFont(font)
self.developer.setText("TextLabel")
self.developer.setObjectName("developer")
self.verticalLayout_2.addWidget(self.developer)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.discount_price = QtWidgets.QLabel(self.widget)
self.price_layout = QtWidgets.QHBoxLayout()
self.price_layout.setObjectName("price_layout")
self.discount_price = QtWidgets.QLabel(self.info_widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -52,15 +58,13 @@ class Ui_WishlistWidget(object):
self.discount_price.setSizePolicy(sizePolicy)
self.discount_price.setText("TextLabel")
self.discount_price.setObjectName("discount_price")
self.horizontalLayout_2.addWidget(self.discount_price)
self.price = QtWidgets.QLabel(self.widget)
self.price_layout.addWidget(self.discount_price)
self.price = QtWidgets.QLabel(self.info_widget)
self.price.setText("TextLabel")
self.price.setObjectName("price")
self.horizontalLayout_2.addWidget(self.price)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout.addWidget(self.widget)
self.price_layout.addWidget(self.price)
self.verticalLayout_2.addLayout(self.price_layout)
self.main_layout.addWidget(self.info_widget, 0, QtCore.Qt.AlignTop)
self.delete_button = QtWidgets.QPushButton(WishlistWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
@ -69,7 +73,7 @@ class Ui_WishlistWidget(object):
self.delete_button.setSizePolicy(sizePolicy)
self.delete_button.setText("")
self.delete_button.setObjectName("delete_button")
self.horizontalLayout.addWidget(self.delete_button)
self.main_layout.addWidget(self.delete_button)
self.retranslateUi(WishlistWidget)

View file

@ -6,16 +6,34 @@
<rect>
<x>0</x>
<y>0</y>
<width>523</width>
<height>172</height>
<width>202</width>
<height>94</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<property name="windowTitle">
<string notr="true">WishlistWIdget</string>
</property>
<layout class="QHBoxLayout" name="main_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="info_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
@ -51,7 +69,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="price_layout">
<item>
<widget class="QLabel" name="discount_price">
<property name="sizePolicy">
@ -74,19 +92,6 @@
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/wishlist.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@ -14,59 +14,24 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Wishlist(object):
def setupUi(self, Wishlist):
Wishlist.setObjectName("Wishlist")
Wishlist.resize(736, 398)
Wishlist.setWindowTitle("StackedWidget")
self.page = QtWidgets.QWidget()
self.page.setObjectName("page")
self.verticalLayout = QtWidgets.QVBoxLayout(self.page)
self.verticalLayout.setObjectName("verticalLayout")
self.scroll_area = QtWidgets.QScrollArea(self.page)
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setObjectName("scroll_area")
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 716, 378))
self.scroll_widget.setObjectName("scroll_widget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scroll_widget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title_label = QtWidgets.QLabel(self.scroll_widget)
font = QtGui.QFont()
font.setPointSize(15)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.verticalLayout_2.addWidget(self.title_label)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.sort_label = QtWidgets.QLabel(self.scroll_widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sort_label.sizePolicy().hasHeightForWidth())
self.sort_label.setSizePolicy(sizePolicy)
self.sort_label.setObjectName("sort_label")
self.horizontalLayout.addWidget(self.sort_label)
self.sort_cb = QtWidgets.QComboBox(self.scroll_widget)
self.sort_cb.setObjectName("sort_cb")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.horizontalLayout.addWidget(self.sort_cb)
self.reverse = QtWidgets.QCheckBox(self.scroll_widget)
self.reverse.setObjectName("reverse")
self.horizontalLayout.addWidget(self.reverse)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.filter_label = QtWidgets.QLabel(self.scroll_widget)
self.filter_label.setObjectName("filter_label")
self.horizontalLayout.addWidget(self.filter_label)
self.filter_cb = QtWidgets.QComboBox(self.scroll_widget)
self.filter_cb.setObjectName("filter_cb")
self.filter_cb.addItem("")
self.filter_cb.addItem("")
self.horizontalLayout.addWidget(self.filter_cb)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.reload_button = QtWidgets.QPushButton(self.scroll_widget)
Wishlist.resize(489, 165)
Wishlist.setWindowTitle("Wishlist")
self.main_layout = QtWidgets.QVBoxLayout(Wishlist)
self.main_layout.setObjectName("main_layout")
self.tool_layout = QtWidgets.QHBoxLayout()
self.tool_layout.setObjectName("tool_layout")
self.filter_combo = QtWidgets.QComboBox(Wishlist)
self.filter_combo.setObjectName("filter_combo")
self.tool_layout.addWidget(self.filter_combo)
self.order_combo = QtWidgets.QComboBox(Wishlist)
self.order_combo.setObjectName("order_combo")
self.tool_layout.addWidget(self.order_combo)
self.reverse_check = QtWidgets.QCheckBox(Wishlist)
self.reverse_check.setObjectName("reverse_check")
self.tool_layout.addWidget(self.reverse_check)
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.tool_layout.addItem(spacerItem)
self.reload_button = QtWidgets.QPushButton(Wishlist)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -74,41 +39,35 @@ class Ui_Wishlist(object):
self.reload_button.setSizePolicy(sizePolicy)
self.reload_button.setText("")
self.reload_button.setObjectName("reload_button")
self.horizontalLayout.addWidget(self.reload_button)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.list_layout = QtWidgets.QVBoxLayout()
self.list_layout.setObjectName("list_layout")
self.verticalLayout_2.addLayout(self.list_layout)
self.no_games_label = QtWidgets.QLabel(self.scroll_widget)
self.tool_layout.addWidget(self.reload_button)
self.main_layout.addLayout(self.tool_layout)
self.scrollarea = QtWidgets.QScrollArea(Wishlist)
self.scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.scrollarea.setWidgetResizable(True)
self.scrollarea.setObjectName("scrollarea")
self.container = QtWidgets.QWidget()
self.container.setGeometry(QtCore.QRect(0, 0, 473, 115))
self.container.setObjectName("container")
self.container_layout = QtWidgets.QVBoxLayout(self.container)
self.container_layout.setObjectName("container_layout")
self.no_games_label = QtWidgets.QLabel(self.container)
self.no_games_label.setObjectName("no_games_label")
self.verticalLayout_2.addWidget(self.no_games_label)
spacerItem2 = QtWidgets.QSpacerItem(379, 218, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem2)
self.scroll_area.setWidget(self.scroll_widget)
self.verticalLayout.addWidget(self.scroll_area)
Wishlist.addWidget(self.page)
self.container_layout.addWidget(self.no_games_label, 0, QtCore.Qt.AlignTop)
self.scrollarea.setWidget(self.container)
self.main_layout.addWidget(self.scrollarea)
self.retranslateUi(Wishlist)
def retranslateUi(self, Wishlist):
_translate = QtCore.QCoreApplication.translate
self.title_label.setText(_translate("Wishlist", "Wishlist"))
self.sort_label.setText(_translate("Wishlist", "Sort by"))
self.sort_cb.setItemText(0, _translate("Wishlist", "Name"))
self.sort_cb.setItemText(1, _translate("Wishlist", "Price"))
self.sort_cb.setItemText(2, _translate("Wishlist", "Developer"))
self.sort_cb.setItemText(3, _translate("Wishlist", "Discount"))
self.reverse.setText(_translate("Wishlist", "Reverse"))
self.filter_label.setText(_translate("Wishlist", "Filter:"))
self.filter_cb.setItemText(0, _translate("Wishlist", "None"))
self.filter_cb.setItemText(1, _translate("Wishlist", "Discount"))
self.reverse_check.setText(_translate("Wishlist", "Reverse"))
self.no_games_label.setText(_translate("Wishlist", "No games matching your filter"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Wishlist = QtWidgets.QStackedWidget()
Wishlist = QtWidgets.QWidget()
ui = Ui_Wishlist()
ui.setupUi(Wishlist)
Wishlist.show()

View file

@ -1,184 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Wishlist</class>
<widget class="QStackedWidget" name="Wishlist">
<widget class="QWidget" name="Wishlist">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>736</width>
<height>398</height>
<width>489</width>
<height>165</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">StackedWidget</string>
<string notr="true">Wishlist</string>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scroll_area">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scroll_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>716</width>
<height>378</height>
</rect>
<layout class="QVBoxLayout" name="main_layout">
<item>
<layout class="QHBoxLayout" name="tool_layout">
<item>
<widget class="QComboBox" name="filter_combo"/>
</item>
<item>
<widget class="QComboBox" name="order_combo"/>
</item>
<item>
<widget class="QCheckBox" name="reverse_check">
<property name="text">
<string>Reverse</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>Wishlist</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="sort_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Sort by</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sort_cb">
<item>
<property name="text">
<string>Name</string>
</property>
</item>
<item>
<property name="text">
<string>Price</string>
</property>
</item>
<item>
<property name="text">
<string>Developer</string>
</property>
</item>
<item>
<property name="text">
<string>Discount</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="reverse">
<property name="text">
<string>Reverse</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="filter_label">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filter_cb">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Discount</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="reload_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="list_layout"/>
</item>
<item>
<widget class="QLabel" name="no_games_label">
<property name="text">
<string>No games matching your filter</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>379</width>
<height>218</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="hspacer_left">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="reload_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollarea">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="container">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>115</height>
</rect>
</property>
<layout class="QVBoxLayout" name="container_layout">
<item alignment="Qt::AlignTop">
<widget class="QLabel" name="no_games_label">
<property name="text">
<string>No games matching your filter</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>

View file

@ -17,7 +17,7 @@ from rare.utils.qt_requests import QtRequests
logger = getLogger("ExtraWidgets")
# FIXME: move this?
class WaitingSpinner(QLabel):
def __init__(self, parent=None):
super(WaitingSpinner, self).__init__(parent=parent)

View file

@ -5,7 +5,7 @@ from typing import Callable, Dict, TypeVar, List, Tuple
from typing import Union
import orjson
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QUrlQuery, pyqtSlot
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QUrlQuery, pyqtSlot, QJsonDocument
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QNetworkDiskCache
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36"
@ -66,7 +66,7 @@ class QtRequests(QObject):
def __post(self, item: RequestQueueItem):
request = self.__prepare_request(item)
payload = orjson.dumps(item.payload) # pylint: disable=maybe-no-member
payload = orjson.dumps(item.payload)
reply = self.manager.post(request, payload)
reply.errorOccurred.connect(self.__on_error)
self.__active_requests[reply] = item

View file

@ -7,3 +7,4 @@ nuitka
ordered-set
PyQt5-stubs
qstylizer
graphql-query

View file

@ -13,3 +13,4 @@ pythonnet>=3.0.0rc4; platform_system == "Windows"
cefpython3; platform_system == "Windows"
pywebview[cef]; platform_system == "Windows"
pypresence

View file

@ -7,3 +7,4 @@ legendary-gl @ git+https://github.com/derrod/legendary@96e07ff ; platform_system
orjson
vdf; platform_system == "Linux" or platform_system == "FreeBSD"
pywin32; platform_system == "Windows"