Merge pull request #388 from loathingKernel/shop_refactor
Refactor the Store page
This commit is contained in:
commit
a35e430539
|
@ -8,7 +8,7 @@ from .downloads import DownloadsTab
|
||||||
from .games import GamesTab
|
from .games import GamesTab
|
||||||
from .settings import SettingsTab
|
from .settings import SettingsTab
|
||||||
from .settings.debug import DebugSettings
|
from .settings.debug import DebugSettings
|
||||||
from .store import Shop
|
from .store import StoreTab
|
||||||
from .tab_widgets import MainTabBar, TabButtonWidget
|
from .tab_widgets import MainTabBar, TabButtonWidget
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class MainTabWidget(QTabWidget):
|
||||||
self.setTabEnabled(self.downloads_index, not self.args.offline)
|
self.setTabEnabled(self.downloads_index, not self.args.offline)
|
||||||
|
|
||||||
if 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.store_index = self.addTab(self.store_tab, self.tr("Store (Beta)"))
|
||||||
self.setTabEnabled(self.store_index, not self.args.offline)
|
self.setTabEnabled(self.store_index, not self.args.offline)
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,42 @@
|
||||||
from PyQt5.QtGui import QShowEvent, QHideEvent
|
from PyQt5.QtGui import QShowEvent, QHideEvent
|
||||||
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
|
|
||||||
from legendary.core import LegendaryCore
|
from legendary.core import LegendaryCore
|
||||||
|
|
||||||
from rare.shared.rare_core import RareCore
|
from rare.widgets.side_tab import SideTabWidget
|
||||||
from rare.utils.paths import cache_dir
|
from .api.models.response import CatalogOfferModel
|
||||||
from .game_info import ShopGameInfo
|
from .landing import LandingWidget, LandingPage
|
||||||
from .search_results import SearchResults
|
from .search import SearchPage
|
||||||
from .shop_api_core import ShopApiCore
|
from .store_api import StoreAPI
|
||||||
from .shop_widget import ShopWidget
|
from .widgets.details import DetailsWidget
|
||||||
from .wishlist import WishlistWidget, Wishlist
|
from .wishlist import WishlistPage
|
||||||
|
|
||||||
|
|
||||||
class Shop(QStackedWidget):
|
class StoreTab(SideTabWidget):
|
||||||
init = False
|
|
||||||
|
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.core = core
|
||||||
self.rcore = RareCore.instance()
|
# self.rcore = RareCore.instance()
|
||||||
self.api_core = ShopApiCore(
|
self.api = StoreAPI(
|
||||||
self.core.egs.session.headers["Authorization"],
|
self.core.egs.session.headers["Authorization"],
|
||||||
self.core.language_code,
|
self.core.language_code,
|
||||||
self.core.country_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.landing = LandingPage(self.api, parent=self)
|
||||||
self.wishlist_widget = Wishlist(self.api_core)
|
self.landing_index = self.addTab(self.landing, self.tr("Store"))
|
||||||
|
|
||||||
self.store_tabs = QTabWidget(parent=self)
|
self.search = SearchPage(self.api, parent=self)
|
||||||
self.store_tabs.addTab(self.shop, self.tr("Games"))
|
self.search_index = self.addTab(self.search, self.tr("Search"))
|
||||||
self.store_tabs.addTab(self.wishlist_widget, self.tr("Wishlist"))
|
|
||||||
|
|
||||||
self.addWidget(self.store_tabs)
|
self.wishlist = WishlistPage(self.api, parent=self)
|
||||||
|
self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"))
|
||||||
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)
|
|
||||||
|
|
||||||
def showEvent(self, a0: QShowEvent) -> None:
|
def showEvent(self, a0: QShowEvent) -> None:
|
||||||
if a0.spontaneous() or self.init:
|
if a0.spontaneous() or self.init:
|
||||||
return super().showEvent(a0)
|
return super().showEvent(a0)
|
||||||
self.shop.load()
|
|
||||||
self.wishlist_widget.update_wishlist()
|
|
||||||
self.init = True
|
self.init = True
|
||||||
return super().showEvent(a0)
|
return super().showEvent(a0)
|
||||||
|
|
||||||
|
@ -64,14 +45,3 @@ class Shop(QStackedWidget):
|
||||||
return super().hideEvent(a0)
|
return super().hideEvent(a0)
|
||||||
# TODO: Implement store unloading
|
# TODO: Implement store unloading
|
||||||
return super().hideEvent(a0)
|
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)
|
|
||||||
|
|
39
rare/components/tabs/store/__main__.py
Normal file
39
rare/components/tabs/store/__main__.py
Normal 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()
|
0
rare/components/tabs/store/api/__init__.py
Normal file
0
rare/components/tabs/store/api/__init__.py
Normal file
568
rare/components/tabs/store/api/constants/queries.py
Normal file
568
rare/components/tabs/store/api/constants/queries.py
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
29
rare/components/tabs/store/api/debug.py
Normal file
29
rare/components/tabs/store/api/debug.py
Normal 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)
|
15
rare/components/tabs/store/api/graphql/.graphqlconfig
Normal file
15
rare/components/tabs/store/api/graphql/.graphqlconfig
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
rare/components/tabs/store/api/graphql/schema.graphql
Normal file
76
rare/components/tabs/store/api/graphql/schema.graphql
Normal 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
|
||||||
|
}
|
0
rare/components/tabs/store/api/models/__init__.py
Normal file
0
rare/components/tabs/store/api/models/__init__.py
Normal file
164
rare/components/tabs/store/api/models/diesel.py
Normal file
164
rare/components/tabs/store/api/models/diesel.py
Normal 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
|
80
rare/components/tabs/store/api/models/query.py
Normal file
80
rare/components/tabs/store/api/models/query.py
Normal 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
|
480
rare/components/tabs/store/api/models/response.py
Normal file
480
rare/components/tabs/store/api/models/response.py
Normal 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
|
5
rare/components/tabs/store/api/models/utils.py
Normal file
5
rare/components/tabs/store/api/models/utils.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(date: str):
|
||||||
|
return datetime.fromisoformat(date[:-1]).replace(tzinfo=timezone.utc)
|
|
@ -44,74 +44,411 @@ class Constants(QObject):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
game_query = (
|
__Image = '''
|
||||||
"query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, "
|
type
|
||||||
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, "
|
url
|
||||||
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean "
|
alt
|
||||||
"= 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 "
|
|
||||||
)
|
|
||||||
|
|
||||||
search_query = (
|
__StorePageMapping = '''
|
||||||
"query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, "
|
cmsSlug
|
||||||
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, "
|
offerId
|
||||||
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean = "
|
prePurchaseOfferId
|
||||||
"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 "
|
|
||||||
)
|
|
||||||
|
|
||||||
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'
|
__PageSandboxModel = '''
|
||||||
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"
|
pageSlug
|
||||||
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"
|
pageType
|
||||||
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"
|
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)
|
||||||
|
|
|
@ -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)
|
|
|
@ -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)
|
|
239
rare/components/tabs/store/landing.py
Normal file
239
rare/components/tabs/store/landing.py
Normal 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)
|
56
rare/components/tabs/store/results.py
Normal file
56
rare/components/tabs/store/results.py
Normal 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)
|
||||||
|
|
219
rare/components/tabs/store/search.py
Normal file
219
rare/components/tabs/store/search.py
Normal 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)
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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
|
|
|
@ -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)
|
|
258
rare/components/tabs/store/store_api.py
Normal file
258
rare/components/tabs/store/store_api.py
Normal 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()
|
0
rare/components/tabs/store/widgets/__init__.py
Normal file
0
rare/components/tabs/store/widgets/__init__.py
Normal file
269
rare/components/tabs/store/widgets/details.py
Normal file
269
rare/components/tabs/store/widgets/details.py
Normal 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)
|
18
rare/components/tabs/store/widgets/groups.py
Normal file
18
rare/components/tabs/store/widgets/groups.py
Normal 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()
|
||||||
|
|
112
rare/components/tabs/store/widgets/image.py
Normal file
112
rare/components/tabs/store/widgets/image.py
Normal 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()
|
136
rare/components/tabs/store/widgets/items.py
Normal file
136
rare/components/tabs/store/widgets/items.py
Normal 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)
|
||||||
|
|
|
@ -1,45 +1,116 @@
|
||||||
from PyQt5.QtCore import pyqtSignal
|
from enum import IntEnum
|
||||||
from PyQt5.QtWidgets import QStackedWidget, QMessageBox
|
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.ui.components.tabs.store.wishlist import Ui_Wishlist
|
||||||
from rare.utils.extra_widgets import WaitingSpinner
|
from rare.utils.misc import qta_icon
|
||||||
from rare.utils.misc import qta_icon as 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):
|
class WishlistPage(SlidingStackedWidget, SideTabContents):
|
||||||
show_game_info = pyqtSignal(dict)
|
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()
|
update_wishlist_signal = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, api_core: ShopApiCore):
|
def __init__(self, api: StoreAPI, parent=None):
|
||||||
super(Wishlist, self).__init__()
|
super(WishlistWidget, self).__init__(parent=parent)
|
||||||
self.api_core = api_core
|
self.implements_scrollarea = True
|
||||||
self.setupUi(self)
|
self.api = api
|
||||||
self.addWidget(WaitingSpinner())
|
self.ui = Ui_Wishlist()
|
||||||
self.setCurrentIndex(1)
|
self.ui.setupUi(self)
|
||||||
self.wishlist = []
|
self.ui.main_layout.setContentsMargins(0, 0, 3, 0)
|
||||||
self.widgets = []
|
|
||||||
|
|
||||||
self.sort_cb.currentIndexChanged.connect(
|
self.wishlist_layout = FlowLayout()
|
||||||
lambda i: self.set_wishlist(self.wishlist, i)
|
self.ui.container_layout.addLayout(self.wishlist_layout, stretch=1)
|
||||||
)
|
|
||||||
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.reverse.stateChanged.connect(
|
filters = {
|
||||||
lambda: self.set_wishlist(sort=self.sort_cb.currentIndex())
|
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):
|
def update_wishlist(self):
|
||||||
self.setCurrentIndex(1)
|
self.setEnabled(False)
|
||||||
self.api_core.get_wishlist(self.set_wishlist)
|
self.api.get_wishlist(self.set_wishlist)
|
||||||
|
|
||||||
def delete_from_wishlist(self, game):
|
def delete_from_wishlist(self, game: CatalogOfferModel):
|
||||||
self.api_core.remove_from_wishlist(
|
self.api.remove_from_wishlist(
|
||||||
game["namespace"],
|
game.namespace,
|
||||||
game["id"],
|
game.id,
|
||||||
lambda success: self.update_wishlist()
|
lambda success: self.update_wishlist()
|
||||||
if success
|
if success
|
||||||
else QMessageBox.warning(
|
else QMessageBox.warning(
|
||||||
|
@ -48,72 +119,68 @@ class Wishlist(QStackedWidget, Ui_Wishlist):
|
||||||
)
|
)
|
||||||
self.update_wishlist_signal.emit()
|
self.update_wishlist_signal.emit()
|
||||||
|
|
||||||
def set_filter(self, i):
|
@pyqtSlot(int)
|
||||||
count = 0
|
def filter_wishlist(self, index: int = int(WishlistFilter.NONE)):
|
||||||
for w in self.widgets:
|
list_filter = self.ui.filter_combo.itemData(index, Qt.UserRole)
|
||||||
if i == 1 and not w.discount:
|
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly)
|
||||||
w.setVisible(False)
|
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:
|
else:
|
||||||
w.setVisible(True)
|
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:
|
@pyqtSlot(int)
|
||||||
w.setVisible(True)
|
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:
|
if list_order == WishlistOrder.NAME:
|
||||||
self.no_games_label.setVisible(True)
|
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:
|
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":
|
if wishlist and wishlist[0] == "error":
|
||||||
return
|
return
|
||||||
|
|
||||||
if wishlist is not None:
|
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindDirectChildrenOnly)
|
||||||
self.wishlist = wishlist
|
for w in widgets:
|
||||||
|
self.wishlist_layout.removeWidget(w)
|
||||||
|
w.deleteLater()
|
||||||
|
|
||||||
for i in self.widgets:
|
self.ui.no_games_label.setVisible(bool(wishlist))
|
||||||
i.deleteLater()
|
|
||||||
|
|
||||||
if sort == 0:
|
for game in wishlist:
|
||||||
sorted_list = sorted(self.wishlist, key=lambda x: x["offer"]["title"])
|
w = WishlistItemWidget(self.api.cached_manager, game.offer, self.ui.container)
|
||||||
elif sort == 1:
|
w.show_details.connect(self.show_details)
|
||||||
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)
|
|
||||||
w.delete_from_wishlist.connect(self.delete_from_wishlist)
|
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.
Binary file not shown.
|
@ -64,6 +64,14 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
|
||||||
css = qstylizer.style.StyleSheet()
|
css = qstylizer.style.StyleSheet()
|
||||||
|
|
||||||
|
|
||||||
|
# Generic flat button
|
||||||
|
css['QPushButton[flat="true"]'].setValues(
|
||||||
|
border="0px",
|
||||||
|
borderRadius="5px",
|
||||||
|
backgroundColor="rgba(255, 255, 255, 5%)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# InfoLabel
|
# InfoLabel
|
||||||
css.QLabel["#InfoLabel"].setValues(
|
css.QLabel["#InfoLabel"].setValues(
|
||||||
color="#999",
|
color="#999",
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
|
|
||||||
/* This file is auto-generated from "stylesheet.py". DO NOT EDIT!!! */
|
/* 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 {
|
QLabel#InfoLabel {
|
||||||
color: #999;
|
color: #999;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
216
rare/ui/components/tabs/store/details.py
Normal file
216
rare/ui/components/tabs/store/details.py
Normal 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_())
|
394
rare/ui/components/tabs/store/details.ui
Normal file
394
rare/ui/components/tabs/store/details.ui
Normal 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>
|
130
rare/ui/components/tabs/store/landing.py
Normal file
130
rare/ui/components/tabs/store/landing.py
Normal 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_())
|
183
rare/ui/components/tabs/store/landing.ui
Normal file
183
rare/ui/components/tabs/store/landing.ui
Normal 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>
|
130
rare/ui/components/tabs/store/search.py
Normal file
130
rare/ui/components/tabs/store/search.py
Normal 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_())
|
183
rare/ui/components/tabs/store/search.ui
Normal file
183
rare/ui/components/tabs/store/search.ui
Normal 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>
|
|
@ -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_())
|
|
|
@ -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>
|
|
|
@ -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_())
|
|
|
@ -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>
|
|
0
rare/ui/components/tabs/store/widgets/__init__.py
Normal file
0
rare/ui/components/tabs/store/widgets/__init__.py
Normal file
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-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
|
# 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.
|
# 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):
|
class Ui_WishlistWidget(object):
|
||||||
def setupUi(self, WishlistWidget):
|
def setupUi(self, WishlistWidget):
|
||||||
WishlistWidget.setObjectName("WishlistWidget")
|
WishlistWidget.setObjectName("WishlistWidget")
|
||||||
WishlistWidget.resize(523, 172)
|
WishlistWidget.resize(202, 94)
|
||||||
WishlistWidget.setWindowTitle("Form")
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(WishlistWidget)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
sizePolicy.setVerticalStretch(0)
|
||||||
self.widget = QtWidgets.QWidget(WishlistWidget)
|
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 = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.info_widget.sizePolicy().hasHeightForWidth())
|
||||||
self.widget.setSizePolicy(sizePolicy)
|
self.info_widget.setSizePolicy(sizePolicy)
|
||||||
self.widget.setObjectName("widget")
|
self.info_widget.setObjectName("info_widget")
|
||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget)
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.info_widget)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
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 = QtGui.QFont()
|
||||||
font.setPointSize(16)
|
font.setPointSize(16)
|
||||||
self.title_label.setFont(font)
|
self.title_label.setFont(font)
|
||||||
|
@ -35,16 +41,16 @@ class Ui_WishlistWidget(object):
|
||||||
self.title_label.setWordWrap(True)
|
self.title_label.setWordWrap(True)
|
||||||
self.title_label.setObjectName("title_label")
|
self.title_label.setObjectName("title_label")
|
||||||
self.verticalLayout_2.addWidget(self.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 = QtGui.QFont()
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
self.developer.setFont(font)
|
self.developer.setFont(font)
|
||||||
self.developer.setText("TextLabel")
|
self.developer.setText("TextLabel")
|
||||||
self.developer.setObjectName("developer")
|
self.developer.setObjectName("developer")
|
||||||
self.verticalLayout_2.addWidget(self.developer)
|
self.verticalLayout_2.addWidget(self.developer)
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
self.price_layout = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
self.price_layout.setObjectName("price_layout")
|
||||||
self.discount_price = QtWidgets.QLabel(self.widget)
|
self.discount_price = QtWidgets.QLabel(self.info_widget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
@ -52,15 +58,13 @@ class Ui_WishlistWidget(object):
|
||||||
self.discount_price.setSizePolicy(sizePolicy)
|
self.discount_price.setSizePolicy(sizePolicy)
|
||||||
self.discount_price.setText("TextLabel")
|
self.discount_price.setText("TextLabel")
|
||||||
self.discount_price.setObjectName("discount_price")
|
self.discount_price.setObjectName("discount_price")
|
||||||
self.horizontalLayout_2.addWidget(self.discount_price)
|
self.price_layout.addWidget(self.discount_price)
|
||||||
self.price = QtWidgets.QLabel(self.widget)
|
self.price = QtWidgets.QLabel(self.info_widget)
|
||||||
self.price.setText("TextLabel")
|
self.price.setText("TextLabel")
|
||||||
self.price.setObjectName("price")
|
self.price.setObjectName("price")
|
||||||
self.horizontalLayout_2.addWidget(self.price)
|
self.price_layout.addWidget(self.price)
|
||||||
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
|
self.verticalLayout_2.addLayout(self.price_layout)
|
||||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
self.main_layout.addWidget(self.info_widget, 0, QtCore.Qt.AlignTop)
|
||||||
self.verticalLayout_2.addItem(spacerItem)
|
|
||||||
self.horizontalLayout.addWidget(self.widget)
|
|
||||||
self.delete_button = QtWidgets.QPushButton(WishlistWidget)
|
self.delete_button = QtWidgets.QPushButton(WishlistWidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
@ -69,7 +73,7 @@ class Ui_WishlistWidget(object):
|
||||||
self.delete_button.setSizePolicy(sizePolicy)
|
self.delete_button.setSizePolicy(sizePolicy)
|
||||||
self.delete_button.setText("")
|
self.delete_button.setText("")
|
||||||
self.delete_button.setObjectName("delete_button")
|
self.delete_button.setObjectName("delete_button")
|
||||||
self.horizontalLayout.addWidget(self.delete_button)
|
self.main_layout.addWidget(self.delete_button)
|
||||||
|
|
||||||
self.retranslateUi(WishlistWidget)
|
self.retranslateUi(WishlistWidget)
|
||||||
|
|
|
@ -6,16 +6,34 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>523</width>
|
<width>202</width>
|
||||||
<height>172</height>
|
<height>94</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="sizePolicy">
|
||||||
<string notr="true">Form</string>
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<property name="windowTitle">
|
||||||
<item>
|
<string notr="true">WishlistWIdget</string>
|
||||||
<widget class="QWidget" name="widget" native="true">
|
</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">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -51,7 +69,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="price_layout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="discount_price">
|
<widget class="QLabel" name="discount_price">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -74,19 +92,6 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'rare/ui/components/tabs/store/wishlist.ui'
|
# 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
|
# 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.
|
# 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):
|
class Ui_Wishlist(object):
|
||||||
def setupUi(self, Wishlist):
|
def setupUi(self, Wishlist):
|
||||||
Wishlist.setObjectName("Wishlist")
|
Wishlist.setObjectName("Wishlist")
|
||||||
Wishlist.resize(736, 398)
|
Wishlist.resize(489, 165)
|
||||||
Wishlist.setWindowTitle("StackedWidget")
|
Wishlist.setWindowTitle("Wishlist")
|
||||||
self.page = QtWidgets.QWidget()
|
self.main_layout = QtWidgets.QVBoxLayout(Wishlist)
|
||||||
self.page.setObjectName("page")
|
self.main_layout.setObjectName("main_layout")
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.page)
|
self.tool_layout = QtWidgets.QHBoxLayout()
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.tool_layout.setObjectName("tool_layout")
|
||||||
self.scroll_area = QtWidgets.QScrollArea(self.page)
|
self.filter_combo = QtWidgets.QComboBox(Wishlist)
|
||||||
self.scroll_area.setWidgetResizable(True)
|
self.filter_combo.setObjectName("filter_combo")
|
||||||
self.scroll_area.setObjectName("scroll_area")
|
self.tool_layout.addWidget(self.filter_combo)
|
||||||
self.scroll_widget = QtWidgets.QWidget()
|
self.order_combo = QtWidgets.QComboBox(Wishlist)
|
||||||
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 716, 378))
|
self.order_combo.setObjectName("order_combo")
|
||||||
self.scroll_widget.setObjectName("scroll_widget")
|
self.tool_layout.addWidget(self.order_combo)
|
||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scroll_widget)
|
self.reverse_check = QtWidgets.QCheckBox(Wishlist)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
self.reverse_check.setObjectName("reverse_check")
|
||||||
self.title_label = QtWidgets.QLabel(self.scroll_widget)
|
self.tool_layout.addWidget(self.reverse_check)
|
||||||
font = QtGui.QFont()
|
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
font.setPointSize(15)
|
self.tool_layout.addItem(spacerItem)
|
||||||
self.title_label.setFont(font)
|
self.reload_button = QtWidgets.QPushButton(Wishlist)
|
||||||
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)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
@ -74,41 +39,35 @@ class Ui_Wishlist(object):
|
||||||
self.reload_button.setSizePolicy(sizePolicy)
|
self.reload_button.setSizePolicy(sizePolicy)
|
||||||
self.reload_button.setText("")
|
self.reload_button.setText("")
|
||||||
self.reload_button.setObjectName("reload_button")
|
self.reload_button.setObjectName("reload_button")
|
||||||
self.horizontalLayout.addWidget(self.reload_button)
|
self.tool_layout.addWidget(self.reload_button)
|
||||||
self.verticalLayout_2.addLayout(self.horizontalLayout)
|
self.main_layout.addLayout(self.tool_layout)
|
||||||
self.list_layout = QtWidgets.QVBoxLayout()
|
self.scrollarea = QtWidgets.QScrollArea(Wishlist)
|
||||||
self.list_layout.setObjectName("list_layout")
|
self.scrollarea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||||
self.verticalLayout_2.addLayout(self.list_layout)
|
self.scrollarea.setWidgetResizable(True)
|
||||||
self.no_games_label = QtWidgets.QLabel(self.scroll_widget)
|
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.no_games_label.setObjectName("no_games_label")
|
||||||
self.verticalLayout_2.addWidget(self.no_games_label)
|
self.container_layout.addWidget(self.no_games_label, 0, QtCore.Qt.AlignTop)
|
||||||
spacerItem2 = QtWidgets.QSpacerItem(379, 218, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
self.scrollarea.setWidget(self.container)
|
||||||
self.verticalLayout_2.addItem(spacerItem2)
|
self.main_layout.addWidget(self.scrollarea)
|
||||||
self.scroll_area.setWidget(self.scroll_widget)
|
|
||||||
self.verticalLayout.addWidget(self.scroll_area)
|
|
||||||
Wishlist.addWidget(self.page)
|
|
||||||
|
|
||||||
self.retranslateUi(Wishlist)
|
self.retranslateUi(Wishlist)
|
||||||
|
|
||||||
def retranslateUi(self, Wishlist):
|
def retranslateUi(self, Wishlist):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
self.title_label.setText(_translate("Wishlist", "Wishlist"))
|
self.reverse_check.setText(_translate("Wishlist", "Reverse"))
|
||||||
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.no_games_label.setText(_translate("Wishlist", "No games matching your filter"))
|
self.no_games_label.setText(_translate("Wishlist", "No games matching your filter"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
Wishlist = QtWidgets.QStackedWidget()
|
Wishlist = QtWidgets.QWidget()
|
||||||
ui = Ui_Wishlist()
|
ui = Ui_Wishlist()
|
||||||
ui.setupUi(Wishlist)
|
ui.setupUi(Wishlist)
|
||||||
Wishlist.show()
|
Wishlist.show()
|
||||||
|
|
|
@ -1,184 +1,92 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>Wishlist</class>
|
<class>Wishlist</class>
|
||||||
<widget class="QStackedWidget" name="Wishlist">
|
<widget class="QWidget" name="Wishlist">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>736</width>
|
<width>489</width>
|
||||||
<height>398</height>
|
<height>165</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string notr="true">StackedWidget</string>
|
<string notr="true">Wishlist</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="page">
|
<layout class="QVBoxLayout" name="main_layout">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<item>
|
||||||
<item>
|
<layout class="QHBoxLayout" name="tool_layout">
|
||||||
<widget class="QScrollArea" name="scroll_area">
|
<item>
|
||||||
<property name="widgetResizable">
|
<widget class="QComboBox" name="filter_combo"/>
|
||||||
<bool>true</bool>
|
</item>
|
||||||
</property>
|
<item>
|
||||||
<widget class="QWidget" name="scroll_widget">
|
<widget class="QComboBox" name="order_combo"/>
|
||||||
<property name="geometry">
|
</item>
|
||||||
<rect>
|
<item>
|
||||||
<x>0</x>
|
<widget class="QCheckBox" name="reverse_check">
|
||||||
<y>0</y>
|
<property name="text">
|
||||||
<width>716</width>
|
<string>Reverse</string>
|
||||||
<height>378</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
</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>
|
</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>
|
</widget>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
</item>
|
||||||
</widget>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -17,7 +17,7 @@ from rare.utils.qt_requests import QtRequests
|
||||||
|
|
||||||
logger = getLogger("ExtraWidgets")
|
logger = getLogger("ExtraWidgets")
|
||||||
|
|
||||||
|
# FIXME: move this?
|
||||||
class WaitingSpinner(QLabel):
|
class WaitingSpinner(QLabel):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(WaitingSpinner, self).__init__(parent=parent)
|
super(WaitingSpinner, self).__init__(parent=parent)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Callable, Dict, TypeVar, List, Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import orjson
|
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
|
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"
|
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):
|
def __post(self, item: RequestQueueItem):
|
||||||
request = self.__prepare_request(item)
|
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 = self.manager.post(request, payload)
|
||||||
reply.errorOccurred.connect(self.__on_error)
|
reply.errorOccurred.connect(self.__on_error)
|
||||||
self.__active_requests[reply] = item
|
self.__active_requests[reply] = item
|
||||||
|
|
|
@ -7,3 +7,4 @@ nuitka
|
||||||
ordered-set
|
ordered-set
|
||||||
PyQt5-stubs
|
PyQt5-stubs
|
||||||
qstylizer
|
qstylizer
|
||||||
|
graphql-query
|
||||||
|
|
|
@ -13,3 +13,4 @@ pythonnet>=3.0.0rc4; platform_system == "Windows"
|
||||||
cefpython3; platform_system == "Windows"
|
cefpython3; platform_system == "Windows"
|
||||||
pywebview[cef]; platform_system == "Windows"
|
pywebview[cef]; platform_system == "Windows"
|
||||||
pypresence
|
pypresence
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@ legendary-gl @ git+https://github.com/derrod/legendary@96e07ff ; platform_system
|
||||||
orjson
|
orjson
|
||||||
vdf; platform_system == "Linux" or platform_system == "FreeBSD"
|
vdf; platform_system == "Linux" or platform_system == "FreeBSD"
|
||||||
pywin32; platform_system == "Windows"
|
pywin32; platform_system == "Windows"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue