1
0
Fork 0
mirror of synced 2024-06-02 10:44:40 +12:00

Merge pull request #99 from Dummerle/dev

Add a shop page to browse games, search games, and see information about them
This commit is contained in:
Dummerle 2021-09-05 22:08:06 +02:00 committed by GitHub
commit 7b0bed5eca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 3102 additions and 41 deletions

View file

@ -144,3 +144,39 @@ class VerifyResult(Enum):
HASH_MISMATCH = 1
FILE_MISSING = 2
OTHER_ERROR = 3
x = {'title': 'Frostpunk',
'id': 'b43c1e1e0ca14b6784b323c59c751136', 'namespace': 'd5241c76f178492ea1540fce45616757',
'description': 'Frostpunk', 'effectiveDate': '2099-01-01T00:00:00.000Z', 'offerType': 'OTHERS', 'expiryDate': None,
'status': 'ACTIVE', 'isCodeRedemptionOnly': True, 'keyImages': [{'type': 'VaultClosed',
'url': 'https://cdn1.epicgames.com/d5241c76f178492ea1540fce45616757/offer/EpicVault_Clean_OPEN_V10_LightsON-1920x1080-75e6d0636a6083944570a1c6f94ead4f.png'},
{'type': 'DieselStoreFrontWide',
'url': 'https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_Frostpunk_wide_2560x1440-ef2f4d458120af0839dde35b1a022828'},
{'type': 'DieselStoreFrontTall',
'url': 'https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_Frostpunk_Tall_1200x1600-c71dc27cfe505c6c662c49011b36a0c5'}],
'seller': {'id': 'o-ufmrk5furrrxgsp5tdngefzt5rxdcn', 'name': 'Epic Dev Test Account'}, 'productSlug': 'frostpunk',
'urlSlug': 'free-games-06', 'url': None,
'items': [{'id': '8341d7c7e4534db7848cc428aa4cbe5a', 'namespace': 'd5241c76f178492ea1540fce45616757'}],
'customAttributes': [{'key': 'com.epicgames.app.freegames.vault.close', 'value': '[]'},
{'key': 'com.epicgames.app.blacklist', 'value': '[]'},
{'key': 'com.epicgames.app.freegames.vault.slug',
'value': 'news/the-epic-mega-sale-returns-for-2021'},
{'key': 'publisherName', 'value': '11 bit studios'}, {'key': 'dupe', 'value': '[]'},
{'key': 'com.epicgames.app.freegames.vault.open', 'value': '[]'},
{'key': 'developerName', 'value': '11 bit studios'},
{'key': 'com.epicgames.app.productSlug', 'value': 'frostpunk'}],
'categories': [{'path': 'freegames/vaulted'}, {'path': 'freegames'}, {'path': 'games'}, {'path': 'applications'}],
'tags': [], 'price': {'totalPrice': {'discountPrice': 0, 'originalPrice': 0, 'voucherDiscount': 0, 'discount': 0,
'currencyCode': 'USD', 'currencyInfo': {'decimals': 2},
'fmtPrice': {'originalPrice': '0', 'discountPrice': '0',
'intermediatePrice': '0'}},
'lineOffers': [{'appliedRules': []}]}, 'promotions': {'promotionalOffers': [],
'upcomingPromotionalOffers': [{
'promotionalOffers': [
{
'startDate': '2021-06-03T15:00:00.000Z',
'endDate': '2021-06-10T15:00:00.000Z',
'discountSetting': {
'discountType': 'PERCENTAGE',
'discountPercentage': 0}}]}]}}

View file

@ -12,6 +12,7 @@ elif os.name == "nt":
cache_dir = os.path.expandvars("%APPDATA%/rare/cache")
else:
cache_dir = os.path.expanduser("~/.cache/rare/")
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
@ -26,3 +27,4 @@ if not os.path.exists(data_dir):
os.makedirs(data_dir)
image_dir = os.path.join(data_dir, "images")

View file

@ -3,16 +3,19 @@
import os
from argparse import ArgumentParser
from rare import __version__
from rare import __version__, data_dir
from rare.utils import singleton, utils
def main():
# CLI Options
parser = ArgumentParser()
parser.add_argument("-V", "--version", action="store_true", help="Shows version and exits")
parser.add_argument("-S", "--silent", action="store_true",
help="Launch Rare in background. Open it from System Tray Icon")
parser.add_argument("--debug", action="store_true", help="Launch in debug mode")
parser.add_argument("--offline", action="store_true", help="Launch Rare in offline mode")
parser.add_argument("--desktop-shortcut", action="store_true", dest="desktop_shortcut",
@ -37,12 +40,12 @@ def main():
print(__version__)
return
try:
# this object only allows one instance pre machine
# this object only allows one instance per machine
me = singleton.SingleInstance()
except singleton.SingleInstanceException:
print("Rare is already running")
with open(os.path.expanduser("~/.cache/rare/lockfile"), "w") as file:
with open(os.path.join(data_dir, "lockfile"), "w") as file:
if args.subparser == "launch":
file.write("launch " + args.app_name)
else:
@ -53,6 +56,7 @@ def main():
if args.subparser == "launch":
args.silent = True
# fix error in cx_freeze
from rare.app import start

View file

@ -10,22 +10,24 @@ from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
from custom_legendary.core import LegendaryCore
from rare import languages_path, resources_path
from rare import languages_path, resources_path, cache_dir
from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.utils.utils import get_lang, load_color_scheme
start_time = time.strftime('%y-%m-%d--%H-%M') # year-month-day-hour-minute
file_name = os.path.expanduser(f"~/.cache/rare/logs/Rare_{start_time}.log")
file_name = os.path.join(cache_dir, f"logs/Rare_{start_time}.log")
if not os.path.exists(os.path.dirname(file_name)):
os.makedirs(os.path.dirname(file_name))
logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO,
filename=file_name,
)
if "--debug" in sys.argv:
logging.basicConfig(format='[%(name)s] %(levelname)s: %(message)s', level=logging.INFO)
else:
logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO,
filename=file_name,
)
logger = logging.getLogger("Rare")

View file

@ -7,6 +7,7 @@ from requests.exceptions import ConnectionError
from custom_legendary.core import LegendaryCore
from rare import image_dir
from rare.components.dialogs.login import LoginDialog
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils.utils import download_images
@ -26,6 +27,7 @@ class ImageThread(QThread):
self.download_progess.emit(100)
class LaunchDialog(QDialog, Ui_LaunchDialog):
quit_app = pyqtSignal(int)
start_app = pyqtSignal(bool)
@ -66,6 +68,7 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
if not os.path.exists(image_dir):
os.makedirs(image_dir)
if not self.offline:
self.image_info.setText(self.tr("Downloading Images"))
self.image_thread = ImageThread(self.core, self)

View file

@ -6,6 +6,7 @@ from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
from custom_legendary.core import LegendaryCore
from rare import data_dir
from rare.components.tab_widget import TabWidget
from rare.utils.rpc import DiscordRPC
@ -59,7 +60,7 @@ class MainWindow(QMainWindow):
self.timer.start(1000)
def timer_finished(self):
file_path = os.path.expanduser("~/.cache/rare/lockfile")
file_path = os.path.join(data_dir, "lockfile")
if os.path.exists(file_path):
file = open(file_path, "r")
action = file.read()

View file

@ -1,5 +1,3 @@
import webbrowser
from PyQt5.QtCore import QSize, pyqtSignal
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction, QShortcut
from qtawesome import icon
@ -13,6 +11,7 @@ from rare.components.tabs.cloud_saves import SyncSaves
from rare.components.tabs.downloads import DownloadTab
from rare.components.tabs.games import GameTab
from rare.components.tabs.settings import SettingsTab
from rare.components.tabs.shop import Shop
from rare.utils import legendary_utils
from rare.utils.models import InstallQueueItemModel, InstallOptionsModel
@ -23,32 +22,26 @@ class TabWidget(QTabWidget):
def __init__(self, core: LegendaryCore, parent, offline):
super(TabWidget, self).__init__(parent=parent)
disabled_tab = 3 if not offline else 1
disabled_tab = 4 if not offline else 1
self.core = core
self.setTabBar(TabBar(disabled_tab))
# Generate Tabs
self.games_tab = GameTab(core, self, offline)
self.addTab(self.games_tab, self.tr("Games"))
if not offline:
updates = self.games_tab.default_widget.game_list.updates
self.downloadTab = DownloadTab(core, updates, self)
self.addTab(self.downloadTab, "Downloads" + (" (" + str(len(updates)) + ")" if len(updates) != 0 else ""))
self.cloud_saves = SyncSaves(core, self)
self.addTab(self.cloud_saves, "Cloud Saves")
self.store = Shop(self.core)
self.addTab(self.store, self.tr("Store (Beta)"))
self.settings = SettingsTab(core, self)
# Space Tab
self.addTab(QWidget(), "")
self.setTabEnabled(disabled_tab, False)
# Buttons
store_button = TabButtonWidget(core, 'fa.shopping-cart', 'Epic Games Store')
store_button.pressed.connect(lambda: webbrowser.open("https://www.epicgames.com/store"))
self.tabBar().setTabButton(disabled_tab, self.tabBar().RightSide, store_button)
# Button
self.account = QWidget()
self.addTab(self.account, "")
self.setTabEnabled(disabled_tab + 1, False)
@ -105,7 +98,7 @@ class TabWidget(QTabWidget):
self.games_tab.default_widget.game_list.game_exited.connect(self.game_finished)
# Open game list on click on Games tab button
self.tabBarClicked.connect(lambda x: self.games_tab.layout.setCurrentIndex(0) if x == 0 else None)
self.tabBarClicked.connect(self.mouse_clicked)
self.setIconSize(QSize(25, 25))
# shortcuts
@ -117,8 +110,15 @@ class TabWidget(QTabWidget):
self.downloadTab.dl_status.connect(
self.games_tab.default_widget.game_list.installing_widget.image_widget.set_status)
def mouse_clicked(self, tab_num):
if tab_num == 0:
self.games_tab.layout.setCurrentIndex(0)
if tab_num == 3:
self.store.load()
# TODO; maybe pass InstallOptionsModel only, not split arguments
def install_game(self, options: InstallOptionsModel, update=False, silent=False):
install_dialog = InstallDialog(self.core,
InstallQueueItemModel(options=options),
update=update, silent=silent, parent=self)
@ -165,8 +165,7 @@ class TabWidget(QTabWidget):
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.downloadTab.update_text.setVisible(len(self.downloadTab.update_widgets) == 0)
# Update gamelist and set text of Downlaods to "Downloads"
# Update gamelist and set text of Downloads to "Downloads"
def dl_finished(self, update_list):
if update_list[0]:
self.games_tab.default_widget.game_list.update_list(update_list[1])

View file

@ -1,3 +1,4 @@
import json
import os
import platform
@ -8,6 +9,7 @@ from qtawesome import icon
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game, InstalledGame
from rare import data_dir
from rare.components.tabs.games.game_info.dlcs import DlcTab
from rare.components.tabs.games.game_info.game_settings import GameSettings
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
@ -71,6 +73,18 @@ class GameInfo(QWidget, Ui_GameInfo):
self.setupUi(self)
self.core = core
self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"),
"silver": self.tr("Silver"),
"bronze": self.tr("Bronze"),
"fail": self.tr("Could not get grade"),
"pending": self.tr("Not enough reports")}
if os.path.exists(p := os.path.join(data_dir, "game_list.json")):
self.grade_table = json.load(open(p))
else:
self.grade_table = {}
if platform.system() == "Windows":
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)

View file

@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QVBoxLayout, QScrollArea, QL
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare import data_dir
from rare.utils.utils import download_image
@ -82,7 +83,7 @@ class DLCWidget(QGroupBox):
super(DLCWidget, self).__init__()
self.main_layout = QHBoxLayout()
self.dlc = dlc
IMAGE_DIR = QSettings().value("img_dir", os.path.expanduser("~/.cache/rare/images"))
IMAGE_DIR = QSettings().value("img_dir", os.path.join(data_dir, "images"))
if installed:
if os.path.exists(os.path.join(IMAGE_DIR, dlc.app_name, "FinalArt.png")):

View file

@ -9,6 +9,7 @@ from qtawesome import icon
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare import data_dir
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabBar
from rare.utils.json_formatter import QJsonModel
@ -71,6 +72,7 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.steam_worker.rating_signal.connect(self.grade.setText)
if platform.system() == "Windows":
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)

View file

@ -5,6 +5,7 @@ from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget
from custom_legendary.core import LegendaryCore
from rare import data_dir
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.components.tabs.games.game_widgets.installed_icon_widget import GameWidgetInstalled
from rare.components.tabs.games.game_widgets.installed_list_widget import InstalledListWidget

View file

@ -8,7 +8,7 @@ from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.Console import ConsoleWindow
from rare.components.extra.console import ConsoleWindow
from rare.utils import legendary_utils
from rare.utils.utils import create_desktop_link
@ -150,8 +150,10 @@ class BaseInstalledWidget(QGroupBox):
def stderr(self):
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8", errors="ignore")
print(stderr)
logger.error(stderr)
# QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
def finished(self, exit_code):
logger.info("Game exited with exit code: " + str(exit_code))
self.finish_signal.emit(self.game.app_name)

View file

@ -8,6 +8,7 @@ from logging import getLogger
from PyQt5.QtCore import QSettings, Qt
from PyQt5.QtWidgets import QWidget
from rare import cache_dir, data_dir
from rare.components.tabs.settings.rpc_settings import RPCSettings
from rare.ui.components.tabs.settings.rare import Ui_RareSettings
from rare.utils import utils
@ -35,13 +36,19 @@ class RareSettings(QWidget, Ui_RareSettings):
(self.auto_sync_cloud, "auto_sync_cloud", True),
(self.notification, "notification", True),
(self.save_size, "save_size", False),
(self.log_games, "show_console", False)
(self.log_games, "show_console", False),
# (self.image_cache, "cache_images", True)
]
self.settings = QSettings()
self.img_dir_path = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str)
self.img_dir_path = self.settings.value("img_dir", os.path.join(data_dir, "images"), type=str)
language = self.settings.value("language", get_lang(), type=str)
self.logdir = os.path.expanduser("~/.cache/rare/logs")
self.logdir = os.path.join(cache_dir, "logs")
# Select Image directory
# self.img_dir = PathEdit(self.img_dir_path, file_type=QFileDialog.DirectoryOnly, save_func=self.save_path)
# self.img_dir_layout.addWidget(self.img_dir)
# Select lang
self.lang_select.addItems([i[1] for i in languages])
@ -85,6 +92,7 @@ class RareSettings(QWidget, Ui_RareSettings):
)
if platform.system() == "Linux":
self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop")
self.start_menu_link = os.path.expanduser("~/.local/share/applications/Rare.desktop")
elif platform.system() == "Windows":
@ -105,7 +113,7 @@ class RareSettings(QWidget, Ui_RareSettings):
self.log_dir_open_button.clicked.connect(self.open_dir)
self.log_dir_clean_button.clicked.connect(self.clean_logdir)
logdir = os.path.expanduser("~/.cache/rare/logs")
logdir = os.path.join(cache_dir, "logs")
# get size of logdir
size = 0
for i in os.listdir(logdir):
@ -116,8 +124,8 @@ class RareSettings(QWidget, Ui_RareSettings):
# self.log_dir_size_label.setVisible(False)
def clean_logdir(self):
for i in os.listdir(os.path.expanduser("~/.cache/rare/logs")):
os.remove(os.path.expanduser("~/.cache/rare/logs/") + i)
for i in os.listdir(os.path.join(cache_dir, "logs")):
os.remove(os.path.join(cache_dir, "logs/") + i)
self.log_dir_size_label.setText("0KB")
def create_start_menu_link(self):

View file

@ -0,0 +1,61 @@
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
from custom_legendary.core import LegendaryCore
from rare import cache_dir
from rare.components.tabs.shop.game_info import ShopGameInfo
from rare.components.tabs.shop.search_results import SearchResults
from rare.components.tabs.shop.shop_api_core import ShopApiCore
from rare.components.tabs.shop.shop_widget import ShopWidget
from rare.components.tabs.shop.wishlist import WishlistWidget, Wishlist
class Shop(QStackedWidget):
init = False
def __init__(self, core: LegendaryCore):
super(Shop, self).__init__()
self.core = core
self.api_core = ShopApiCore(self.core.egs.session.headers["Authorization"])
self.shop = ShopWidget(cache_dir, core, self.api_core)
self.wishlist_widget = Wishlist(self.api_core)
self.store_tabs = QTabWidget()
self.store_tabs.addTab(self.shop, self.tr("Games"))
self.store_tabs.addTab(self.wishlist_widget, self.tr("Wishlist"))
# self.store_tabs.addTab(self.browse_games, self.tr("Browse"))
self.addWidget(self.store_tabs)
self.search_results = SearchResults(self.api_core)
self.addWidget(self.search_results)
self.search_results.show_info.connect(self.show_game_info)
self.info = ShopGameInfo([i.asset_info.namespace for i in self.core.get_game_list(True)], 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 update_wishlist(self):
self.shop.update_wishlist()
def load(self):
if not self.init:
self.init = True
self.shop.load()
self.wishlist_widget.update_wishlist()
def show_game_info(self, data):
self.info.update_game(data)
self.setCurrentIndex(2)
def show_search_results(self, text: str):
self.search_results.load_results(text)
self.setCurrentIndex(1)

View file

@ -0,0 +1,110 @@
from PyQt5.QtCore import QObject
# Class to use QObject.tr()
class Constants(QObject):
def __init__(self):
super(Constants, self).__init__()
self.categories = sorted([
(self.tr("Action"), "1216"),
(self.tr("Adventure"), "1117"),
(self.tr("Puzzle"), "1298"),
(self.tr("Open world"), "1307"),
(self.tr("Racing"), "1212"),
(self.tr("RPG"), "1367"),
(self.tr("Shooter"), "1210"),
(self.tr("Strategy"), "1115"),
(self.tr("Survival"), "1080"),
(self.tr("First Person"), "1294"),
(self.tr("Indie"), "1263"),
(self.tr("Simulation"), "1393"),
(self.tr("Sport"), "1283")
], key=lambda x: x[0])
self.platforms = [
("MacOS", "9548"),
("Windows", "9547"),
]
self.others = [
(self.tr("Single player"), "1370"),
(self.tr("Multiplayer"), "1203"),
(self.tr("Controller"), "9549"),
(self.tr("Co-op"), "1264"),
]
self.types = [
(self.tr("Editor"), "editors"),
(self.tr("Game"), "games/edition/base"),
(self.tr("Bundle"), "bundles/games"),
(self.tr("Add-on"), "addons"),
(self.tr("Apps"), "software/edition/base")
]
game_query = "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, " \
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, " \
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean " \
"= false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, " \
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n " \
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n " \
"locale: $locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n " \
"sortDir: $sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n " \
"priceRange: $priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: " \
"$effectiveDate\n ) {\n elements {\n title\n id\n namespace\n " \
"description\n effectiveDate\n keyImages {\n type\n url\n }\n " \
" currentPrice\n seller {\n id\n name\n }\n productSlug\n " \
" urlSlug\n url\n tags {\n id\n }\n items {\n id\n " \
" namespace\n }\n customAttributes {\n key\n value\n }\n " \
"categories {\n path\n }\n catalogNs @include(if: $withMapping) {\n " \
"mappings(pageType: \"productHome\") {\n pageSlug\n pageType\n }\n " \
"}\n offerMappings @include(if: $withMapping) {\n pageSlug\n pageType\n " \
"}\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n " \
"discountPrice\n originalPrice\n voucherDiscount\n discount\n " \
" currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(" \
"locale: $locale) {\n originalPrice\n discountPrice\n " \
"intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n " \
" id\n endDate\n discountSetting {\n discountType\n " \
" }\n }\n }\n }\n promotions(category: $category) @include(if: " \
"$withPromotions) {\n promotionalOffers {\n promotionalOffers {\n " \
"startDate\n endDate\n discountSetting {\n discountType\n " \
" discountPercentage\n }\n }\n }\n " \
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n " \
"endDate\n discountSetting {\n discountType\n " \
"discountPercentage\n }\n }\n }\n }\n }\n paging {\n " \
" count\n total\n }\n }\n }\n}\n "
search_query = "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, " \
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, " \
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean = " \
"false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, " \
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n " \
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n locale: " \
"$locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n sortDir: " \
"$sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n priceRange: " \
"$priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: $effectiveDate\n ) {" \
"\n elements {\n title\n id\n namespace\n description\n " \
"effectiveDate\n keyImages {\n type\n url\n }\n currentPrice\n " \
"seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n " \
" tags {\n id\n }\n items {\n id\n namespace\n }\n " \
"customAttributes {\n key\n value\n }\n categories {\n path\n " \
"}\n catalogNs @include(if: $withMapping) {\n mappings(pageType: \"productHome\") {\n " \
" pageSlug\n pageType\n }\n }\n offerMappings @include(if: $withMapping) " \
"{\n pageSlug\n pageType\n }\n price(country: $country) @include(if: " \
"$withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n " \
"voucherDiscount\n discount\n currencyCode\n currencyInfo {\n " \
"decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n " \
"discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n " \
" appliedRules {\n id\n endDate\n discountSetting {\n " \
"discountType\n }\n }\n }\n }\n promotions(category: " \
"$category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n " \
" startDate\n endDate\n discountSetting {\n " \
"discountType\n discountPercentage\n }\n }\n }\n " \
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n " \
"endDate\n discountSetting {\n discountType\n discountPercentage\n " \
" }\n }\n }\n }\n }\n paging {\n count\n " \
"total\n }\n }\n }\n}\n "
wishlist_query = "\n query wishlistQuery($country:String!, $locale:String) {\n Wishlist {\n wishlistItems {\n elements {\n id\n order\n created\n offerId\n updated\n namespace\n \n offer {\n productSlug\n urlSlug\n title\n id\n namespace\n offerType\n expiryDate\n status\n isCodeRedemptionOnly\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n catalogNs {\n mappings(pageType: \"productHome\") {\n pageSlug\n pageType\n }\n }\n offerMappings {\n pageSlug\n pageType\n }\n categories {\n path\n }\n price(country: $country) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n currencyCode\n currencyInfo {\n decimals\n symbol\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n }\n }\n }\n }\n\n }\n }\n }\n }\n"
add_to_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"
remove_from_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"
coupon_query = "\n query getCoupons($currencyCountry: String!, $identityId: String!, $locale: String) {\n CodeRedemption {\n coupons(currencyCountry: $currencyCountry, identityId: $identityId, includeSalesEventInfo: true) {\n code\n codeStatus\n codeType\n consumptionMetadata {\n amountDisplay {\n amount\n currency\n placement\n symbol\n }\n minSalesPriceDisplay {\n amount\n currency\n placement\n symbol\n }\n }\n endDate\n namespace\n salesEvent(locale: $locale) {\n eventName\n eventSlug\n voucherImages {\n type\n url\n }\n voucherLink\n }\n startDate\n }\n }\n }\n"

View file

@ -0,0 +1,189 @@
import logging
import webbrowser
from PyQt5.QtGui import QPixmap, QFont
from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, QHBoxLayout
from qtawesome import icon
from rare.components.tabs.shop.shop_models import ShopGame
from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info
from rare.utils.extra_widgets import WaitingSpinner, ImageLabel
from rare.utils.utils import get_lang
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.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(WaitingSpinner())
self.locale = get_lang()
self.wishlist_button.clicked.connect(self.add_to_wishlist)
self.in_wishlist = False
self.wishlist = []
def handle_wishlist_update(self, data):
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().setParent(None)
slug = data["productSlug"]
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)
self.dev.setText(self.tr("Loading"))
self.price.setText(self.tr("Loading"))
# 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
self.api_core.get_game(slug, is_bundle, 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):
self.game = ShopGame.from_json(game, self.data)
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)
min_label = QLabel(self.tr("Minimum"))
min_label.setFont(bold_font)
rec_label = QLabel(self.tr("Recommend"))
rec_label.setFont(bold_font)
if self.game.reqs:
self.req_group_box.layout().addWidget(min_label, 0, 1)
self.req_group_box.layout().addWidget(rec_label, 0, 2)
for i, (key, value) in enumerate(self.game.reqs.get("Windows", {}).items()):
self.req_group_box.layout().addWidget(QLabel(key), i + 1, 0)
min_label = QLabel(value[0])
min_label.setWordWrap(True)
self.req_group_box.layout().addWidget(min_label, i + 1, 1)
rec_label = QLabel(value[1])
rec_label.setWordWrap(True)
self.req_group_box.layout().addWidget(rec_label, i + 1, 2)
else:
self.req_group_box.layout().addWidget(QLabel(self.tr("Could not get requirements")))
if self.game.image_urls.front_tall:
img = self.game.image_urls.front_tall
elif self.game.image_urls.offer_image_tall:
img = self.game.image_urls.offer_image_tall
else:
img = ""
self.image.update_image(img, 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
QWidget().setLayout(self.social_link_gb.layout())
self.social_link_gb.setLayout(QHBoxLayout())
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", scale_factor=1.5)
else:
try:
icn = icon("mdi." + 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):
webbrowser.open("https://www.epicgames.com/store/de/p/" + self.slug)
class SocialButton(QPushButton):
def __init__(self, icn, url):
super(SocialButton, self).__init__(icn, "")
self.url = url
self.clicked.connect(lambda: webbrowser.open(url))
self.setToolTip(url)

View file

@ -0,0 +1,121 @@
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 qtawesome import icon
from rare.components.tabs.shop.shop_models import ImageUrlModel
from rare.ui.components.tabs.store.wishlist_widget import Ui_WishlistWidget
from rare.utils.extra_widgets import ImageLabel
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)
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:
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"]:
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"]]))
# print(json_info["keyImages"])
self.setLayout(self.layout)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
self.show_info.emit(self.json_info)
class WishlistWidget(QWidget, Ui_WishlistWidget):
open_game = pyqtSignal(dict)
delete_from_wishlist = pyqtSignal(dict)
def __init__(self, game: dict):
super(WishlistWidget, self).__init__()
self.setupUi(self)
self.game = game
self.title_label.setText(game.get("title"))
for attr in game["customAttributes"]:
if attr["key"] == "developerName":
self.developer.setText(attr["value"])
break
else:
self.developer.setText(game["seller"]["name"])
original_price = game['price']['totalPrice']['fmtPrice']['originalPrice']
discount_price = game['price']['totalPrice']['fmtPrice']['discountPrice']
self.price.setText(original_price if original_price != "0" else self.tr("Free"))
# if discount
if original_price != discount_price:
self.discount = True
font = QFont()
font.setStrikeOut(True)
self.price.setFont(font)
self.discount_price.setText(discount_price)
else:
self.discount = False
self.discount_price.setVisible(False)
self.image = ImageLabel()
self.layout().insertWidget(0, self.image)
image_model = ImageUrlModel.from_json(game["keyImages"])
url = image_model.front_wide
if not url:
url = image_model.offer_image_wide
self.image.update_image(url, game.get("title"), (240, 135))
self.delete_button.setIcon(icon("mdi.delete", color="white"))
self.delete_button.clicked.connect(lambda: self.delete_from_wishlist.emit(self.game))
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
# left button
if e.button() == 1:
self.open_game.emit(self.game)
# right
elif e.button() == 2:
pass # self.showMenu(e)

View file

@ -0,0 +1,102 @@
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, FlowLayout, WaitingSpinner
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):
QVBoxLayout().addWidget(self.widget)
self.widget = QWidget()
self.layout = FlowLayout()
if not results:
self.layout.addWidget(QLabel(self.tr("No results found")))
else:
for res in results:
w = _SearchResultItem(res)
w.show_info.connect(self.show_info.emit)
self.layout.addWidget(w)
self.widget.setLayout(self.layout)
self.result_area.setWidget(self.widget)
self.setCurrentIndex(0)
class _SearchResultItem(QGroupBox):
res: dict
show_info = pyqtSignal(dict)
def __init__(self, result: dict):
super(_SearchResultItem, self).__init__()
self.layout = QVBoxLayout()
self.image = ImageLabel()
for img in result["keyImages"]:
if img["type"] == "DieselStoreFrontTall":
width = 240
self.image.update_image(img["url"], result["title"], (width, 360))
break
else:
print("No image found")
self.layout.addWidget(self.image)
self.res = result
self.title = QLabel(self.res["title"])
title_font = QFont()
title_font.setPixelSize(15)
self.title.setFont(title_font)
self.title.setWordWrap(True)
self.layout.addWidget(self.title)
price = result['price']['totalPrice']['fmtPrice']['originalPrice']
discount_price = result['price']['totalPrice']['fmtPrice']['discountPrice']
price_layout = QHBoxLayout()
price_label = QLabel(price if price != "0" else self.tr("Free"))
price_layout.addWidget(price_label)
if price != discount_price:
font = QFont()
font.setStrikeOut(True)
price_label.setFont(font)
price_layout.addWidget(QLabel(discount_price))
# self.discount_price = QLabel(f"{self.tr('Discount price: ')}{discount_price}")
self.layout.addLayout(price_layout)
self.setLayout(self.layout)
self.setFixedWidth(260)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.button() == 1:
self.show_info.emit(self.res)

View file

@ -0,0 +1,137 @@
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QObject
from rare.components.tabs.shop.constants import wishlist_query, search_query, game_query, add_to_wishlist_query, \
remove_from_wishlist_query
from rare.components.tabs.shop.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequestManager
from rare.utils.utils import get_lang
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
class ShopApiCore(QObject):
update_wishlist = pyqtSignal()
def __init__(self, auth_token):
super(ShopApiCore, self).__init__()
self.token = auth_token
self.manager = QtRequestManager()
self.auth_manager = QtRequestManager(authorization_token=auth_token)
self.locale = get_lang()
self.browse_active = False
self.next_browse_request = tuple(())
def get_free_games(self, handle_func: callable):
url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions"
self.manager.get(url, lambda data: self._handle_free_games(data, handle_func))
def _handle_free_games(self, data, handle_func):
try:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
except KeyError as e:
logger.error(str(e))
def get_wishlist(self, handle_func):
self.auth_manager.post(graphql_url, {
"query": wishlist_query,
"variables": {
"country": self.locale.upper(),
"locale": self.locale
}
}, lambda data: self._handle_wishlist(data, handle_func))
def _handle_wishlist(self, data, handle_func):
handle_func(data["data"]["Wishlist"]["wishlistItems"]["elements"])
def search_game(self, name, handle_func):
payload = {
"query": search_query,
"variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base", "count": 1,
"country": "DE", "keywords": name, "locale": self.locale, "sortDir": "DESC",
"allowCountries": self.locale.upper(),
"start": 0, "tag": "", "withMapping": False, "withPrice": True}
}
self.manager.post(graphql_url, payload, lambda data: self._handle_search(data, handle_func))
def _handle_search(self, data, handle_func):
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
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
payload = {
"variables": browse_model.__dict__,
"query": game_query
}
self.auth_manager.post(graphql_url, payload, lambda data: self._handle_browse_games(data, handle_func))
def _handle_browse_games(self, data, handle_func):
self.browse_active = False
if not self.next_browse_request:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
else:
self.browse_games(*self.next_browse_request)
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):
handle_func(data)
def add_to_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"variables": {
"offerId": offer_id,
"namespace": namespace,
"country": self.locale.upper(),
"locale": self.locale
},
"query": add_to_wishlist_query
}
self.auth_manager.post(graphql_url, payload, lambda data: self._handle_add_to_wishlist(data, handle_func))
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, payload, lambda data: self._handle_remove_from_wishlist(data, handle_func))
def _handle_remove_from_wishlist(self, data, handle_func):
try:
data = data["data"]["Wishlist"]["removeFromWishlist"]
if data["success"]:
handle_func(True)
else:
handle_func(False)
except Exception as e:
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()

View file

@ -0,0 +1,144 @@
import datetime
import random
from dataclasses import dataclass
from rare.utils.utils import get_lang
class ImageUrlModel:
def __init__(self, front_tall: str = "", offer_image_tall: str = "",
thumbnail: str = "", front_wide: str = "", offer_image_wide: 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
@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"]
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
locale: str = get_lang()
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%X')}.{str(random.randint(0, 999)).zfill(3)}Z]"
price: str = ""
onSale: bool = False
@property
def __dict__(self):
payload = {"category": self.category,
"count": self.count,
"country": self.locale.upper(),
"keywords": self.keywords,
"locale": self.locale,
"sortDir": self.sortDir,
"allowCountries": self.locale.upper(),
"start": self.start,
"tag": self.tag,
"withMapping": self.withMapping,
"withPrice": self.withPrice,
"releaseDate": self.date,
"effectiveDate": self.date,
}
if self.price == "free":
payload["freeGame"] = True
elif self.price.startswith("<price>"):
payload["priceRange"] = self.price.replace("<price>", "")
if self.onSale:
payload["onSale"] = True
return payload

View file

@ -0,0 +1,275 @@
import datetime
import logging
import random
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QGroupBox, QScrollArea, QCheckBox, QVBoxLayout, QLabel
from custom_legendary.core import LegendaryCore
from rare.components.tabs.shop import ShopApiCore
from rare.components.tabs.shop.constants import Constants
from rare.components.tabs.shop.game_widgets import GameWidget
from rare.components.tabs.shop.shop_models import BrowseModel
from rare.ui.components.tabs.store.store import Ui_ShopWidget
from rare.utils.extra_widgets import WaitingSpinner, FlowLayout, ButtonLineEdit
from rare.utils.utils import get_lang
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.locale = get_lang()
self.update_games_allowed = True
self.free_widget.setLayout(FlowLayout())
self.free_games_now = QGroupBox(self.tr("Now Free"))
self.free_games_now.setLayout(FlowLayout())
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(FlowLayout())
self.free_widget.layout().addWidget(self.coming_free_games)
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.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)
self.prepare_request()
def update_wishlist(self):
self.api_core.get_wishlist(self.add_wishlist_items)
def add_wishlist_items(self, wishlist):
QWidget().setLayout(self.discount_widget.layout())
self.discount_widget.setLayout(FlowLayout())
discounts = 0
for game in wishlist:
if not game:
continue
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
self.discounts_gb.setVisible(discounts > 0)
self.discount_stack.setCurrentIndex(0)
def add_free_games(self, free_games):
date = datetime.datetime.now()
free_games_now = []
coming_free_games = []
for game in free_games:
if game["title"] == "Mystery Game":
coming_free_games.append(game)
continue
try:
# parse datetime to check if game is next week or now
try:
end_date = datetime.datetime.strptime(
game["promotions"]["upcomingPromotionalOffers"][0]["promotionalOffers"][0]["endDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception:
try:
end_date = datetime.datetime.strptime(
game["promotions"]["promotionalOffers"][0]["promotionalOffers"][0]["endDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception:
continue
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:
print(e)
logger.warning(str(e))
continue
except TypeError:
print("type error")
continue
if start_date < date < end_date:
free_games_now.append(game)
elif start_date > date:
coming_free_games.append(game)
# free games now
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)
# self.free_games_now.layout().addStretch(1)
# 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):
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("")
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)
locale = get_lang()
self.game_stack.setCurrentIndex(1)
date = f"[,{datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%dT%X')}.{str(random.randint(0, 999)).zfill(3)}Z]"
browse_model = BrowseModel(locale=locale, date=date, 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):
QWidget().setLayout(self.game_widget.layout())
if data:
self.game_widget.setLayout(FlowLayout())
for game in data:
price = game['price']['totalPrice']['fmtPrice']['originalPrice']
discount_price = game['price']['totalPrice']['fmtPrice']['discountPrice']
if price != discount_price:
w = GameWidget(self.path, game, 275)
else:
w = GameWidget(self.path, game, 275)
self.game_widget.layout().addWidget(w)
w.show_info.connect(self.show_game.emit)
else:
self.game_widget.setLayout(QVBoxLayout())
self.game_widget.layout().addWidget(
QLabel(self.tr("Could not get games matching the filter")))
self.game_widget.layout().addStretch(1)
self.game_stack.setCurrentIndex(0)
class CheckBox(QCheckBox):
activated = pyqtSignal(str)
deactivated = pyqtSignal(str)
def __init__(self, text, tag):
super(CheckBox, self).__init__(text)
self.tag = tag
self.toggled.connect(self.handle_toggle)
def handle_toggle(self):
if self.isChecked():
self.activated.emit(self.tag)
else:
self.deactivated.emit(self.tag)

View file

@ -0,0 +1,88 @@
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QStackedWidget, QMessageBox
from qtawesome import icon
from rare.components.tabs.shop import ShopApiCore
from rare.components.tabs.shop.game_widgets import WishlistWidget
from rare.ui.components.tabs.store.wishlist import Ui_Wishlist
from rare.utils.extra_widgets import WaitingSpinner
class Wishlist(QStackedWidget, Ui_Wishlist):
show_game_info = pyqtSignal(dict)
update_wishlist_signal = pyqtSignal()
def __init__(self, api_core: ShopApiCore):
super(Wishlist, self).__init__()
self.api_core = api_core
self.setupUi(self)
self.addWidget(WaitingSpinner())
self.setCurrentIndex(1)
self.wishlist = []
self.widgets = []
self.sort_cb.currentIndexChanged.connect(lambda i: self.set_wishlist(self.wishlist, i))
self.filter_cb.currentIndexChanged.connect(self.set_filter)
self.reload_button.clicked.connect(self.update_wishlist)
self.reload_button.setIcon(icon("fa.refresh", color="white"))
def update_wishlist(self):
self.setCurrentIndex(1)
self.api_core.get_wishlist(self.set_wishlist)
def delete_from_wishlist(self, game):
self.api_core.remove_from_wishlist(game["namespace"], game["id"],
lambda success: self.update_wishlist() if success else
QMessageBox.warning(self, "Error",
self.tr("Could not remove game from wishlist")))
self.update_wishlist_signal.emit()
def set_filter(self, i):
count = 0
for w in self.widgets:
if i == 1 and not w.discount:
w.setVisible(False)
else:
w.setVisible(True)
count += 1
if i == 0:
w.setVisible(True)
if count == 0:
self.no_games_label.setVisible(True)
else:
self.no_games_label.setVisible(False)
def set_wishlist(self, wishlist, sort=0):
self.wishlist = wishlist
for i in self.widgets:
i.setParent(None)
if sort == 0:
sorted_list = sorted(wishlist, key=lambda x: x["offer"]["title"])
elif sort == 1:
sorted_list = sorted(wishlist, key=lambda x: x["offer"]['price']['totalPrice']['fmtPrice']['discountPrice'])
elif sort == 2:
sorted_list = sorted(wishlist, key=lambda x: x["offer"]["seller"]["name"])
elif sort == 3:
sorted_list = sorted(wishlist, reverse=True, key=lambda x: 1 - (
x["offer"]["price"]["totalPrice"]["discountPrice"] / x["offer"]["price"]["totalPrice"][
"originalPrice"]))
else:
sorted_list = wishlist
self.widgets.clear()
if len(sorted_list) == 0:
self.no_games_label.setVisible(True)
else:
self.no_games_label.setVisible(False)
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)
self.setCurrentIndex(0)

View file

@ -255,8 +255,8 @@
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>

View file

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'browse_games.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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_browse_games(object):
def setupUi(self, browse_games):
browse_games.setObjectName("browse_games")
browse_games.resize(706, 541)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(browse_games)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.stack = QtWidgets.QStackedWidget(browse_games)
self.stack.setObjectName("stack")
self.games_page = QtWidgets.QWidget()
self.games_page.setObjectName("games_page")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.games_page)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.games = QtWidgets.QScrollArea(self.games_page)
self.games.setWidgetResizable(True)
self.games.setObjectName("games")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 462, 503))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.games.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_5.addWidget(self.games)
self.stack.addWidget(self.games_page)
self.error = QtWidgets.QWidget()
self.error.setObjectName("error")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.error)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.error_label = QtWidgets.QLabel(self.error)
self.error_label.setObjectName("error_label")
self.verticalLayout_6.addWidget(self.error_label)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_6.addItem(spacerItem)
self.stack.addWidget(self.error)
self.horizontalLayout_2.addWidget(self.stack)
self.filter_scroll = QtWidgets.QScrollArea(browse_games)
self.filter_scroll.setMaximumSize(QtCore.QSize(200, 16777215))
self.filter_scroll.setWidgetResizable(True)
self.filter_scroll.setObjectName("filter_scroll")
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 198, 521))
self.scroll_widget.setObjectName("scroll_widget")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scroll_widget)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.price_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.price_gb.setObjectName("price_gb")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.price_gb)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.clear_price = QtWidgets.QRadioButton(self.price_gb)
self.clear_price.setChecked(True)
self.clear_price.setObjectName("clear_price")
self.verticalLayout_2.addWidget(self.clear_price)
self.free_button = QtWidgets.QRadioButton(self.price_gb)
self.free_button.setObjectName("free_button")
self.verticalLayout_2.addWidget(self.free_button)
self.under10 = QtWidgets.QRadioButton(self.price_gb)
self.under10.setObjectName("under10")
self.verticalLayout_2.addWidget(self.under10)
self.under20 = QtWidgets.QRadioButton(self.price_gb)
self.under20.setObjectName("under20")
self.verticalLayout_2.addWidget(self.under20)
self.under30 = QtWidgets.QRadioButton(self.price_gb)
self.under30.setObjectName("under30")
self.verticalLayout_2.addWidget(self.under30)
self.above = QtWidgets.QRadioButton(self.price_gb)
self.above.setObjectName("above")
self.verticalLayout_2.addWidget(self.above)
self.on_discount = QtWidgets.QRadioButton(self.price_gb)
self.on_discount.setObjectName("on_discount")
self.verticalLayout_2.addWidget(self.on_discount)
self.verticalLayout_3.addWidget(self.price_gb)
self.genre_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.genre_gb.setObjectName("genre_gb")
self.verticalLayout = QtWidgets.QVBoxLayout(self.genre_gb)
self.verticalLayout.setObjectName("verticalLayout")
self.verticalLayout_3.addWidget(self.genre_gb)
self.type_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.type_gb.setObjectName("type_gb")
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.type_gb)
self.verticalLayout_8.setObjectName("verticalLayout_8")
self.verticalLayout_3.addWidget(self.type_gb)
self.platform_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.platform_gb.setObjectName("platform_gb")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.platform_gb)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.verticalLayout_3.addWidget(self.platform_gb)
self.others_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.others_gb.setObjectName("others_gb")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.others_gb)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.verticalLayout_3.addWidget(self.others_gb)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem1)
self.filter_scroll.setWidget(self.scroll_widget)
self.horizontalLayout_2.addWidget(self.filter_scroll)
self.retranslateUi(browse_games)
self.stack.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(browse_games)
def retranslateUi(self, browse_games):
_translate = QtCore.QCoreApplication.translate
browse_games.setWindowTitle(_translate("browse_games", "Form"))
self.error_label.setText(_translate("browse_games", "An error occured"))
self.price_gb.setTitle(_translate("browse_games", "Price"))
self.clear_price.setText(_translate("browse_games", "Clear price filter"))
self.free_button.setText(_translate("browse_games", "Free"))
self.under10.setText(_translate("browse_games", "Under 10"))
self.under20.setText(_translate("browse_games", "Under 20"))
self.under30.setText(_translate("browse_games", "Under 30"))
self.above.setText(_translate("browse_games", "14.99 and above"))
self.on_discount.setText(_translate("browse_games", "Discount"))
self.genre_gb.setTitle(_translate("browse_games", "Genre"))
self.type_gb.setTitle(_translate("browse_games", "Type"))
self.platform_gb.setTitle(_translate("browse_games", "Platform"))
self.others_gb.setTitle(_translate("browse_games", "Other Tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
browse_games = QtWidgets.QWidget()
ui = Ui_browse_games()
ui.setupUi(browse_games)
browse_games.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>browse_games</class>
<widget class="QWidget" name="browse_games">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<height>541</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QStackedWidget" name="stack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="games_page">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QScrollArea" name="games">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>503</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="error">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="error_label">
<property name="text">
<string>An error occured</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>
</widget>
</widget>
</item>
<item>
<widget class="QScrollArea" name="filter_scroll">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scroll_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>198</width>
<height>521</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="price_gb">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="clear_price">
<property name="text">
<string>Clear price filter</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="QRadioButton" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_gb">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_gb">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_gb">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_gb">
<property name="title">
<string>Other Tags</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4"/>
</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>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'shop_game_info.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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)
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.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.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.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.setObjectName("discount_price")
self.verticalLayout_2.addWidget(self.discount_price)
self.tags = QtWidgets.QLabel(shop_info)
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)
QtCore.QMetaObject.connectSlotsByName(shop_info)
def retranslateUi(self, shop_info):
_translate = QtCore.QCoreApplication.translate
shop_info.setWindowTitle(_translate("shop_info", "Form"))
self.back_button.setText(_translate("shop_info", "Back"))
self.title.setText(_translate("shop_info", "Error"))
self.dev.setText(_translate("shop_info", "TextLabel"))
self.owned_label.setText(_translate("shop_info", "You already own this game"))
self.price.setText(_translate("shop_info", "TextLabel"))
self.discount_price.setText(_translate("shop_info", "TextLabel"))
self.tags.setText(_translate("shop_info", "TextLabel"))
self.open_store_button.setText(_translate("shop_info", "Buy Game in Epic Games Store"))
self.wishlist_button.setText(_translate("shop_info", "Add to wishlist"))
self.req_group_box.setTitle(_translate("shop_info", "Requirements"))
self.social_link_gb.setTitle(_translate("shop_info", "Social Links"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
shop_info = QtWidgets.QWidget()
ui = Ui_shop_info()
ui.setupUi(shop_info)
shop_info.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,191 @@
<?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>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>Error</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>TextLabel</string>
</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>TextLabel</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>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tags">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open_store_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Buy Game in Epic Games Store</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="wishlist_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add to wishlist</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="req_group_box">
<property name="title">
<string>Requirements</string>
</property>
<layout class="QGridLayout" name="gridLayout_2"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="social_link_gb">
<property name="title">
<string>Social Links</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'store.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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)
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()
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.free_widget.sizePolicy().hasHeightForWidth())
self.free_widget.setSizePolicy(sizePolicy)
self.free_widget.setObjectName("free_widget")
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.free_widget)
self.verticalLayout_8.setObjectName("verticalLayout_8")
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)
QtCore.QMetaObject.connectSlotsByName(ShopWidget)
def retranslateUi(self, ShopWidget):
_translate = QtCore.QCoreApplication.translate
ShopWidget.setWindowTitle(_translate("ShopWidget", "Form"))
self.free_game_group_box.setTitle(_translate("ShopWidget", "Free Games"))
self.discounts_gb.setTitle(_translate("ShopWidget", "Discounts from your wishlist"))
self.filter_game_gb.setTitle(_translate("ShopWidget", "Games"))
self.filter_gb.setTitle(_translate("ShopWidget", "Filter"))
self.reset_button.setText(_translate("ShopWidget", "Reset"))
self.price_gb.setTitle(_translate("ShopWidget", "Price"))
self.none_price.setText(_translate("ShopWidget", "None"))
self.free_button.setText(_translate("ShopWidget", "Free"))
self.under10.setText(_translate("ShopWidget", "Under 10"))
self.under20.setText(_translate("ShopWidget", "Under 20"))
self.under30.setText(_translate("ShopWidget", "Under 30"))
self.above.setText(_translate("ShopWidget", "14.99 and above"))
self.on_discount.setText(_translate("ShopWidget", "Discount"))
self.platform_gb.setTitle(_translate("ShopWidget", "Platform"))
self.genre_gb.setTitle(_translate("ShopWidget", "Genre"))
self.type_gb.setTitle(_translate("ShopWidget", "Type"))
self.others_gb.setTitle(_translate("ShopWidget", "Other Tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ShopWidget = QtWidgets.QWidget()
ui = Ui_ShopWidget()
ui.setupUi(ShopWidget)
ShopWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,248 @@
<?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>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">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred"
vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout"
name="verticalLayout_8"/>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="discounts_gb">
<property name="title">
<string>Discounts from your wishlist</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QStackedWidget" name="discount_stack">
<widget class="QWidget" name="discount_widget"/>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="filter_game_gb">
<property name="title">
<string>Games</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QStackedWidget" name="game_stack">
<widget class="QWidget" name="game_widget"/>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="filter_gb">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Filter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="price_gb">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QRadioButton" name="none_price">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="free_button">
<property name="text">
<string>Free</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under10">
<property name="text">
<string>Under 10</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under20">
<property name="text">
<string>Under 20</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under30">
<property name="text">
<string>Under 30</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="above">
<property name="text">
<string>14.99 and above</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_gb">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_gb">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_gb">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_gb">
<property name="title">
<string>Other Tags</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'wishlist.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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_Wishlist(object):
def setupUi(self, Wishlist):
Wishlist.setObjectName("Wishlist")
Wishlist.resize(736, 398)
self.page = QtWidgets.QWidget()
self.page.setObjectName("page")
self.verticalLayout = QtWidgets.QVBoxLayout(self.page)
self.verticalLayout.setObjectName("verticalLayout")
self.scroll_area = QtWidgets.QScrollArea(self.page)
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setObjectName("scroll_area")
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 716, 378))
self.scroll_widget.setObjectName("scroll_widget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scroll_widget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title_label = QtWidgets.QLabel(self.scroll_widget)
font = QtGui.QFont()
font.setPointSize(15)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.verticalLayout_2.addWidget(self.title_label)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.sort_label = QtWidgets.QLabel(self.scroll_widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sort_label.sizePolicy().hasHeightForWidth())
self.sort_label.setSizePolicy(sizePolicy)
self.sort_label.setObjectName("sort_label")
self.horizontalLayout.addWidget(self.sort_label)
self.sort_cb = QtWidgets.QComboBox(self.scroll_widget)
self.sort_cb.setObjectName("sort_cb")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.sort_cb.addItem("")
self.horizontalLayout.addWidget(self.sort_cb)
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.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.reload_button.sizePolicy().hasHeightForWidth())
self.reload_button.setSizePolicy(sizePolicy)
self.reload_button.setText("")
self.reload_button.setObjectName("reload_button")
self.horizontalLayout.addWidget(self.reload_button)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.list_layout = QtWidgets.QVBoxLayout()
self.list_layout.setObjectName("list_layout")
self.verticalLayout_2.addLayout(self.list_layout)
self.no_games_label = QtWidgets.QLabel(self.scroll_widget)
self.no_games_label.setObjectName("no_games_label")
self.verticalLayout_2.addWidget(self.no_games_label)
spacerItem2 = QtWidgets.QSpacerItem(379, 218, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem2)
self.scroll_area.setWidget(self.scroll_widget)
self.verticalLayout.addWidget(self.scroll_area)
Wishlist.addWidget(self.page)
self.retranslateUi(Wishlist)
QtCore.QMetaObject.connectSlotsByName(Wishlist)
def retranslateUi(self, Wishlist):
_translate = QtCore.QCoreApplication.translate
Wishlist.setWindowTitle(_translate("Wishlist", "StackedWidget"))
self.title_label.setText(_translate("Wishlist", "Wishlist"))
self.sort_label.setText(_translate("Wishlist", "Sort by"))
self.sort_cb.setItemText(0, _translate("Wishlist", "Name"))
self.sort_cb.setItemText(1, _translate("Wishlist", "Price"))
self.sort_cb.setItemText(2, _translate("Wishlist", "Developer"))
self.sort_cb.setItemText(3, _translate("Wishlist", "Discount"))
self.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"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Wishlist = QtWidgets.QStackedWidget()
ui = Ui_Wishlist()
ui.setupUi(Wishlist)
Wishlist.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Wishlist</class>
<widget class="QStackedWidget" name="Wishlist">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>736</width>
<height>398</height>
</rect>
</property>
<property name="windowTitle">
<string>StackedWidget</string>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scroll_area">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scroll_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>716</width>
<height>378</height>
</rect>
</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>
<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>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'wishlist_widget.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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_WishlistWidget(object):
def setupUi(self, WishlistWidget):
WishlistWidget.setObjectName("WishlistWidget")
WishlistWidget.resize(523, 172)
self.horizontalLayout = QtWidgets.QHBoxLayout(WishlistWidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.widget = QtWidgets.QWidget(WishlistWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.widget.setSizePolicy(sizePolicy)
self.widget.setObjectName("widget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title_label = QtWidgets.QLabel(self.widget)
font = QtGui.QFont()
font.setPointSize(16)
self.title_label.setFont(font)
self.title_label.setWordWrap(True)
self.title_label.setObjectName("title_label")
self.verticalLayout_2.addWidget(self.title_label)
self.developer = QtWidgets.QLabel(self.widget)
font = QtGui.QFont()
font.setPointSize(12)
self.developer.setFont(font)
self.developer.setObjectName("developer")
self.verticalLayout_2.addWidget(self.developer)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.discount_price = QtWidgets.QLabel(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.discount_price.sizePolicy().hasHeightForWidth())
self.discount_price.setSizePolicy(sizePolicy)
self.discount_price.setObjectName("discount_price")
self.horizontalLayout_2.addWidget(self.discount_price)
self.price = QtWidgets.QLabel(self.widget)
self.price.setObjectName("price")
self.horizontalLayout_2.addWidget(self.price)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout.addWidget(self.widget)
self.delete_button = QtWidgets.QPushButton(WishlistWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.delete_button.sizePolicy().hasHeightForWidth())
self.delete_button.setSizePolicy(sizePolicy)
self.delete_button.setText("")
self.delete_button.setObjectName("delete_button")
self.horizontalLayout.addWidget(self.delete_button)
self.retranslateUi(WishlistWidget)
QtCore.QMetaObject.connectSlotsByName(WishlistWidget)
def retranslateUi(self, WishlistWidget):
_translate = QtCore.QCoreApplication.translate
WishlistWidget.setWindowTitle(_translate("WishlistWidget", "Form"))
self.title_label.setText(_translate("WishlistWidget", "TextLabel"))
self.developer.setText(_translate("WishlistWidget", "TextLabel"))
self.discount_price.setText(_translate("WishlistWidget", "TextLabel"))
self.price.setText(_translate("WishlistWidget", "TextLabel"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
WishlistWidget = QtWidgets.QWidget()
ui = Ui_WishlistWidget()
ui.setupUi(WishlistWidget)
WishlistWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WishlistWidget</class>
<widget class="QWidget" name="WishlistWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>523</width>
<height>172</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="developer">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="discount_price">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="price">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="delete_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,13 +1,20 @@
import io
import os
from logging import getLogger
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal
from PyQt5.QtGui import QMovie
import PIL
from PIL import Image
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QSettings
from PyQt5.QtGui import QMovie, QPixmap
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QPushButton, \
QStyleOptionTab, QStylePainter, QTabBar
QStyleOptionTab, QStylePainter, QTabBar, QLineEdit, QToolButton
from qtawesome import icon
from rare import resources_path
from rare import resources_path, cache_dir
from rare.ui.utils.pathedit import Ui_PathEdit
from rare.utils.qt_requests import QtRequestManager
logger = getLogger("ExtraWidgets")
class FlowLayout(QLayout):
@ -252,3 +259,82 @@ class SelectViewWidget(QWidget):
self.list_view.setIcon(icon("fa5s.list", color="orange"))
self.icon_view = True
self.toggled.emit()
class ImageLabel(QLabel):
def __init__(self):
super(ImageLabel, self).__init__()
self.path = cache_dir
self.manager = QtRequestManager("bytes")
def update_image(self, url, name, size: tuple = (240, 320)):
self.setFixedSize(*size)
self.img_size = size
self.name = name
for c in r'<>?":|\/* ':
self.name = self.name.replace(c, "")
if self.img_size[0] > self.img_size[1]:
name_extension = "wide"
else:
name_extension = "tall"
self.name = f"{self.name}_{name_extension}.png"
if not os.path.exists(os.path.join(self.path, self.name)):
self.manager.get(url, self.image_ready)
# self.request.finished.connect(self.image_ready)
else:
self.show_image()
def image_ready(self, data):
try:
self.setPixmap(QPixmap())
except RuntimeError:
return
try:
image: Image.Image = Image.open(io.BytesIO(data))
except PIL.UnidentifiedImageError:
print(self.name)
return
image = image.resize((self.img_size[0], self.img_size[1]))
if QSettings().value("cache_images", False, bool):
image.save(os.path.join(self.path, self.name), format="png")
byte_array = io.BytesIO()
image.save(byte_array, format="PNG")
# pixmap = QPixmap.fromImage(ImageQt(image))
pixmap = QPixmap()
pixmap.loadFromData(byte_array.getvalue())
# pixmap = QPixmap.fromImage(ImageQt.ImageQt(image))
self.setPixmap(pixmap)
def show_image(self):
self.image = QPixmap(os.path.join(self.path, self.name)).scaled(*self.img_size,
transformMode=Qt.SmoothTransformation)
self.setPixmap(self.image)
class ButtonLineEdit(QLineEdit):
buttonClicked = pyqtSignal()
def __init__(self, icon_name, placeholder_text: str, parent=None):
super(ButtonLineEdit, self).__init__(parent)
self.button = QToolButton(self)
self.button.setIcon(icon(icon_name, color="white"))
self.button.setStyleSheet('border: 0px; padding: 0px;')
self.button.setCursor(Qt.ArrowCursor)
self.button.clicked.connect(self.buttonClicked.emit)
self.setPlaceholderText(placeholder_text)
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
buttonSize = self.button.sizeHint()
self.setStyleSheet('QLineEdit {padding-right: %dpx; }' % (buttonSize.width() + frameWidth + 1))
self.setMinimumSize(max(self.minimumSizeHint().width(), buttonSize.width() + frameWidth * 2 + 2),
max(self.minimumSizeHint().height(), buttonSize.height() + frameWidth * 2 + 2))
def resizeEvent(self, event):
buttonSize = self.button.sizeHint()
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
self.button.move(self.rect().right() - frameWidth - buttonSize.width(),
(self.rect().bottom() - buttonSize.height() + 1) / 2)
super(ButtonLineEdit, self).resizeEvent(event)

92
rare/utils/qt_requests.py Normal file
View file

@ -0,0 +1,92 @@
import json
from dataclasses import dataclass
from logging import getLogger
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QJsonParseError, QJsonDocument
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
logger = getLogger("QtRequests")
class QtRequestManager(QObject):
data_ready = pyqtSignal(object)
request = None
request_active = None
def __init__(self, type: str = "json", authorization_token: str = None):
super(QtRequestManager, self).__init__()
self.manager = QNetworkAccessManager()
self.type = type
self.authorization_token = authorization_token
self.request_queue = []
def post(self, url: str, payload: dict, handle_func):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
self.request_active = RequestQueueItem(handle_func=handle_func)
payload = json.dumps(payload).encode("utf-8")
request.setHeader(QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36")
if self.authorization_token is not None:
request.setRawHeader(b"Authorization", self.authorization_token.encode())
self.request = self.manager.post(request, payload)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(
RequestQueueItem(method="post", url=url, payload=payload, handle_func=handle_func))
def get(self, url: str, handle_func: callable):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36")
self.request_active = RequestQueueItem(handle_func=handle_func)
self.request = self.manager.get(request)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(RequestQueueItem(method="get", url=url, handle_func=handle_func))
def prepare_data(self):
# self.request_active = False
data = {} if self.type == "json" else b""
if self.request:
try:
if self.request.error() == QNetworkReply.NoError:
if self.type == "json":
error = QJsonParseError()
json_data = QJsonDocument.fromJson(self.request.readAll().data(), error)
if QJsonParseError.NoError == error.error:
data = json.loads(json_data.toJson().data().decode())
else:
logger.error(error.errorString())
else:
data = self.request.readAll().data()
except RuntimeError as e:
logger.error(str(e))
self.request_active.handle_func(data)
self.request.deleteLater()
self.request_active = None
if self.request_queue:
if self.request_queue[0].method == "post":
self.post(self.request_queue[0].url, self.request_queue[0].payload, self.request_queue[0].handle_func)
else:
self.get(self.request_queue[0].url, self.request_queue[0].handle_func)
self.request_queue.pop(0)
@dataclass
class RequestQueueItem:
method: str = None
url: str = None
handle_func: callable = None
payload: dict = None

View file

@ -9,6 +9,8 @@ from PyQt5.QtCore import QThread, pyqtSignal
from custom_legendary.core import LegendaryCore
from rare import cache_dir, data_dir
from rare import data_dir, cache_dir
replace_chars = ",;.:-_ "
file = os.path.join(cache_dir, "game_list.json")
@ -77,6 +79,7 @@ def get_grade(steam_code):
def load_json() -> dict:
if not os.path.exists(file):
response = requests.get(url)
steam_ids = json.loads(response.text)["applist"]["apps"]
ids = {}
@ -108,6 +111,7 @@ def get_steam_id(title: str):
ids = json.loads(open(file, "r").read())
if title in ids.keys():
steam_name = [title]
else:
steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
if steam_name:

View file

@ -26,6 +26,7 @@ logger = getLogger("Utils")
settings = QSettings("Rare", "Rare")
def download_images(signal: pyqtSignal, core: LegendaryCore):
if not os.path.isdir(image_dir):
os.makedirs(image_dir)
@ -288,6 +289,7 @@ def create_rare_desktop_link(type_of_link):
def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -> bool:
igame = core.get_installed_game(app_name)
if os.path.exists(p := os.path.join(image_dir, igame.app_name, 'Thumbnail.png')):
icon = p
elif os.path.exists(p := os.path.join(image_dir, igame.app_name, "DieselGameBoxLogo.png")):
@ -295,6 +297,7 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -
else:
icon = os.path.join(os.path.join(image_dir, igame.app_name, "DieselGameBoxTall.png"))
icon = icon.replace(".png", "")
# Linux
if platform.system() == "Linux":
if type_of_link == "desktop":
@ -387,6 +390,7 @@ def get_uninstalled_pixmap(app_name: str) -> QPixmap:
return pixmap
def optimal_text_background(image: list) -> Tuple[int, int, int]:
"""
Finds an optimal background color for text on the image by calculating the
@ -419,3 +423,4 @@ def text_color_for_background(background: Tuple[int, int, int]) -> Tuple[int,
return 255, 255, 255
else:
return 0, 0, 0