Merge branch 'shop' into update_shop
This commit is contained in:
commit
c212904cc0
|
@ -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}}]}]}}
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -3,15 +3,17 @@
|
|||
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",
|
||||
|
@ -36,12 +38,12 @@ def main():
|
|||
print(__version__)
|
||||
exit(0)
|
||||
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 +55,8 @@ def main():
|
|||
if args.subparser == "launch":
|
||||
args.silent = True
|
||||
|
||||
# start app
|
||||
# Import start now, to not import everything
|
||||
from rare.app import start
|
||||
start(args)
|
||||
|
||||
|
|
17
rare/app.py
17
rare/app.py
|
@ -10,21 +10,24 @@ from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
|
|||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from rare import languages_path, resources_path
|
||||
|
||||
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")
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import webbrowser
|
||||
|
||||
from PyQt5.QtCore import QSize, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction
|
||||
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,11 +98,17 @@ 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))
|
||||
|
||||
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)
|
||||
|
@ -155,8 +154,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])
|
||||
|
|
|
@ -8,6 +8,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
|
||||
|
@ -72,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)
|
||||
|
|
|
@ -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")):
|
||||
|
|
|
@ -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
|
||||
|
@ -66,6 +67,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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,6 +7,7 @@ from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
|
|||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
from rare import cache_dir
|
||||
from rare.components.dialogs.uninstall_dialog import UninstallDialog
|
||||
from rare.components.extra.Console import ConsoleWindow
|
||||
from rare.utils import legendary_utils
|
||||
|
@ -150,8 +151,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)
|
||||
|
|
|
@ -8,7 +8,6 @@ from qtawesome import icon
|
|||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
|
||||
from rare.utils.extra_widgets import ClickableLabel
|
||||
|
||||
logger = getLogger("GameWidgetInstalled")
|
||||
|
||||
|
@ -36,7 +35,7 @@ class GameWidgetInstalled(BaseInstalledWidget):
|
|||
if self.pixmap:
|
||||
w = 200
|
||||
self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation)
|
||||
self.image = ClickableLabel()
|
||||
self.image = QLabel()
|
||||
self.image.setObjectName("game_widget")
|
||||
self.image.setPixmap(self.pixmap)
|
||||
self.layout.addWidget(self.image)
|
||||
|
|
|
@ -5,7 +5,6 @@ from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
|||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import Game
|
||||
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
|
||||
from rare.utils.extra_widgets import ClickableLabel
|
||||
|
||||
logger = getLogger("Uninstalled")
|
||||
|
||||
|
@ -19,7 +18,7 @@ class IconWidgetUninstalled(BaseUninstalledWidget):
|
|||
if self.pixmap:
|
||||
w = 200
|
||||
self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3))
|
||||
self.image = ClickableLabel()
|
||||
self.image = QLabel()
|
||||
self.image.setPixmap(self.pixmap)
|
||||
self.layout.addWidget(self.image)
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ import sys
|
|||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QSettings, Qt
|
||||
from PyQt5.QtWidgets import QFileDialog, QWidget
|
||||
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
|
||||
from rare.utils.extra_widgets import PathEdit
|
||||
from rare.utils.utils import get_lang, get_possible_langs, get_color_schemes, get_style_sheets
|
||||
|
||||
logger = getLogger("RareSettings")
|
||||
|
@ -36,17 +36,18 @@ 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)
|
||||
# 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])
|
||||
|
@ -90,6 +91,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":
|
||||
|
@ -110,7 +112,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):
|
||||
|
@ -121,8 +123,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):
|
||||
|
|
60
rare/components/tabs/shop/__init__.py
Normal file
60
rare/components/tabs/shop/__init__.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from rare import cache_dir
|
||||
from rare.components.tabs.shop.browse_games import BrowseGames
|
||||
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
|
||||
|
||||
|
||||
class Shop(QStackedWidget):
|
||||
init = False
|
||||
|
||||
def __init__(self, core: LegendaryCore):
|
||||
super(Shop, self).__init__()
|
||||
self.core = core
|
||||
|
||||
self.shop_api = ShopApiCore(self.core.egs.session.headers["Authorization"])
|
||||
|
||||
self.shop = ShopWidget(cache_dir, core, self.shop_api)
|
||||
self.browse_games = BrowseGames(cache_dir, self.shop_api)
|
||||
|
||||
self.store_tabs = QTabWidget()
|
||||
self.store_tabs.addTab(self.shop, self.tr("Games"))
|
||||
self.store_tabs.addTab(self.browse_games, self.tr("Browse"))
|
||||
self.store_tabs.tabBarClicked.connect(lambda x: self.browse_games.load() if x == 1 else None)
|
||||
|
||||
self.addWidget(self.store_tabs)
|
||||
|
||||
self.search_results = SearchResults(self.shop_api)
|
||||
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.shop_api)
|
||||
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.shop.show_game.connect(self.show_game_info)
|
||||
self.browse_games.show_game.connect(self.show_game_info)
|
||||
self.shop_api.update_wishlist.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()
|
||||
|
||||
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)
|
130
rare/components/tabs/shop/browse_games.py
Normal file
130
rare/components/tabs/shop/browse_games.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
import datetime
|
||||
import logging
|
||||
import random
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QWidget, QCheckBox, QVBoxLayout, QLabel
|
||||
|
||||
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.browse_games import Ui_browse_games
|
||||
from rare.utils.extra_widgets import FlowLayout, WaitingSpinner
|
||||
from rare.utils.utils import get_lang
|
||||
|
||||
logger = logging.getLogger("BrowseGames")
|
||||
|
||||
|
||||
class BrowseGames(QWidget, Ui_browse_games):
|
||||
show_game = pyqtSignal(dict)
|
||||
init = False
|
||||
price = ""
|
||||
tags = []
|
||||
types = []
|
||||
|
||||
def __init__(self, path, api_core):
|
||||
super(BrowseGames, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.api_core = api_core
|
||||
self.path = path
|
||||
self.games_widget = QWidget()
|
||||
self.games_widget.setLayout(FlowLayout())
|
||||
self.games.setWidget(self.games_widget)
|
||||
|
||||
self.stack.addWidget(WaitingSpinner())
|
||||
|
||||
self.clear_price.toggled.connect(lambda: self.prepare_request("") if self.clear_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)
|
||||
|
||||
constants = Constants()
|
||||
|
||||
for groupbox, variables in [(self.genre_gb, constants.categories),
|
||||
(self.platform_gb, constants.platforms),
|
||||
(self.others_gb, constants.others)]:
|
||||
|
||||
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)
|
||||
|
||||
for text, tag in constants.types:
|
||||
checkbox = CheckBox(text, tag)
|
||||
checkbox.activated.connect(lambda x: self.prepare_request(added_type=x))
|
||||
checkbox.deactivated.connect(lambda x: self.prepare_request(removed_type=x))
|
||||
self.type_gb.layout().addWidget(checkbox)
|
||||
|
||||
def load(self):
|
||||
if not self.init:
|
||||
self.prepare_request()
|
||||
self.init = True
|
||||
|
||||
def prepare_request(self, price: str = None, added_tag: int = 0, removed_tag: int = 0,
|
||||
added_type: str = "", removed_type: str = ""):
|
||||
|
||||
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)
|
||||
|
||||
locale = get_lang()
|
||||
self.stack.setCurrentIndex(2)
|
||||
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=30, price=self.price)
|
||||
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.games_widget.layout())
|
||||
|
||||
if data:
|
||||
self.games_widget.setLayout(FlowLayout())
|
||||
|
||||
for game in data:
|
||||
w = GameWidget(self.path, game, 275)
|
||||
self.games_widget.layout().addWidget(w)
|
||||
w.show_info.connect(self.show_game.emit)
|
||||
|
||||
else:
|
||||
self.games_widget.setLayout(QVBoxLayout())
|
||||
self.games_widget.layout().addWidget(
|
||||
QLabel(self.tr("Could not get games matching the filter")))
|
||||
self.games_widget.layout().addStretch(1)
|
||||
self.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)
|
109
rare/components/tabs/shop/constants.py
Normal file
109
rare/components/tabs/shop/constants.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
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"
|
147
rare/components/tabs/shop/game_info.py
Normal file
147
rare/components/tabs/shop/game_info.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
import logging
|
||||
import webbrowser
|
||||
|
||||
from PyQt5.QtGui import QPixmap, QFont
|
||||
from PyQt5.QtWidgets import QWidget, QLabel
|
||||
|
||||
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":
|
||||
self.price.setText(self.game.price)
|
||||
else:
|
||||
self.price.setText(self.tr("Free"))
|
||||
|
||||
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)
|
||||
# print(self.game.reqs)
|
||||
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")))
|
||||
|
||||
self.image.update_image(self.game.image_urls.front_tall, 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))
|
||||
self.price.setText(self.game.price)
|
||||
|
||||
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)
|
84
rare/components/tabs/shop/game_widgets.py
Normal file
84
rare/components/tabs/shop/game_widgets.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
import logging
|
||||
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
|
||||
|
||||
from rare.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)
|
||||
|
||||
self.title_label = QLabel(json_info.get("title"))
|
||||
self.title_label.setWordWrap(True)
|
||||
self.layout.addWidget(self.title_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)
|
||||
|
||||
@classmethod
|
||||
def from_request(cls, name, path, shop_api):
|
||||
c = cls(path)
|
||||
shop_api.search_game(name, c.handle_response)
|
||||
return c
|
||||
|
||||
def handle_response(self, data):
|
||||
|
||||
data = data["data"]["Catalog"]["searchStore"]["elements"][0]
|
||||
self.init_ui(data)
|
||||
|
||||
|
||||
class GameWidgetDiscount(GameWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GameWidgetDiscount, self).__init__(*args, **kwargs)
|
||||
|
||||
h_layout = QHBoxLayout()
|
||||
self.layout.addLayout(h_layout)
|
||||
|
||||
price = args[1]['price']['totalPrice']['fmtPrice']['originalPrice']
|
||||
discount_price = args[1]['price']['totalPrice']['fmtPrice']['discountPrice']
|
||||
|
||||
price_label = QLabel(price)
|
||||
|
||||
font = QFont()
|
||||
font.setStrikeOut(True)
|
||||
price_label.setFont(font)
|
||||
h_layout.addWidget(QLabel(discount_price if discount_price != "0" else self.tr("Free")))
|
||||
h_layout.addWidget(price_label)
|
103
rare/components/tabs/shop/search_results.py
Normal file
103
rare/components/tabs/shop/search_results.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
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):
|
||||
results = results["data"]["Catalog"]["searchStore"]["elements"]
|
||||
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)
|
137
rare/components/tabs/shop/shop_api_core.py
Normal file
137
rare/components/tabs/shop/shop_api_core.py
Normal 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)
|
||||
|
||||
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()
|
137
rare/components/tabs/shop/shop_models.py
Normal file
137
rare/components/tabs/shop/shop_models.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
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 = ""):
|
||||
self.front_tall = front_tall
|
||||
self.offer_image_tall = offer_image_tall
|
||||
self.thumbnail = thumbnail
|
||||
self.front_wide = front_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"]
|
||||
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():
|
||||
api_data = api_data["pages"][0]
|
||||
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 = ""
|
||||
|
||||
@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>", "")
|
||||
elif self.price == "sale":
|
||||
payload["onSale"] = True
|
||||
return payload
|
169
rare/components/tabs/shop/shop_widget.py
Normal file
169
rare/components/tabs/shop/shop_widget.py
Normal file
|
@ -0,0 +1,169 @@
|
|||
import datetime
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QStringListModel
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager
|
||||
from PyQt5.QtWidgets import QWidget, QCompleter, QGroupBox, QHBoxLayout, QScrollArea
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from rare.components.tabs.shop import ShopApiCore
|
||||
from rare.components.tabs.shop.constants import search_query
|
||||
from rare.components.tabs.shop.game_widgets import GameWidget, GameWidgetDiscount
|
||||
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.shop_api = shop_api
|
||||
self.manager = QNetworkAccessManager()
|
||||
self.free_games_widget = QWidget()
|
||||
self.free_games_widget.setLayout(FlowLayout())
|
||||
self.free_games_now = QGroupBox(self.tr("Free Games"))
|
||||
self.free_games_now.setLayout(QHBoxLayout())
|
||||
self.free_games_widget.layout().addWidget(self.free_games_now)
|
||||
self.coming_free_games = QGroupBox(self.tr("Free Games next week"))
|
||||
self.coming_free_games.setLayout(QHBoxLayout())
|
||||
self.free_games_widget.layout().addWidget(self.coming_free_games)
|
||||
self.free_games_stack.addWidget(WaitingSpinner())
|
||||
self.free_games_stack.addWidget(self.free_games_widget)
|
||||
|
||||
self.completer = QCompleter()
|
||||
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
|
||||
self.data = []
|
||||
|
||||
self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search Games"))
|
||||
self.scrollAreaWidgetContents.layout().insertWidget(0, self.search_bar)
|
||||
|
||||
# self.search_bar.textChanged.connect(self.search_games)
|
||||
|
||||
self.search_bar.setCompleter(self.completer)
|
||||
self.search_bar.returnPressed.connect(self.show_search_results)
|
||||
self.search_bar.buttonClicked.connect(self.show_search_results)
|
||||
|
||||
# self.search_bar.textChanged.connect(self.load_completer)
|
||||
self.wishlist_gb.setLayout(FlowLayout())
|
||||
self.wishlist_gb.setVisible(False)
|
||||
self.locale = get_lang()
|
||||
|
||||
def load_completer(self, text):
|
||||
if text != "":
|
||||
|
||||
payload = {
|
||||
"query": search_query,
|
||||
"variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base",
|
||||
"count": 20,
|
||||
"country": self.locale.upper(), "keywords": text, "locale": self.locale,
|
||||
"sortDir": "DESC",
|
||||
"allowCountries": self.locale.upper(),
|
||||
"start": 0, "tag": "", "withMapping": False, "withPrice": True}
|
||||
}
|
||||
self.search_request_manager.post("https://www.epicgames.com/graphql", payload)
|
||||
|
||||
def load(self):
|
||||
# load free games
|
||||
self.shop_api.get_free_games(self.add_free_games)
|
||||
# load wishlist
|
||||
self.shop_api.get_wishlist(self.add_wishlist_items)
|
||||
|
||||
def update_wishlist(self):
|
||||
self.shop_api.get_wishlist(self.add_wishlist_items)
|
||||
|
||||
def add_wishlist_items(self, wishlist):
|
||||
QWidget().setLayout(self.wishlist_gb.layout())
|
||||
|
||||
self.wishlist_gb.setLayout(FlowLayout())
|
||||
discounts = 0
|
||||
for game in wishlist:
|
||||
if game["offer"]["price"]["totalPrice"]["discount"] > 0:
|
||||
w = GameWidgetDiscount(self.path, game["offer"])
|
||||
w.show_info.connect(self.show_game.emit)
|
||||
self.wishlist_gb.layout().addWidget(w)
|
||||
discounts += 1
|
||||
self.wishlist_gb.setVisible(discounts > 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
|
||||
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)
|
||||
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)
|
||||
|
||||
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)
|
||||
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.free_game_widgets.append(w)
|
||||
self.coming_free_games.layout().addStretch(1)
|
||||
# self.coming_free_games.setFixedWidth(int(40 + len(coming_free_games) * 300))
|
||||
self.free_games_stack.setCurrentIndex(1)
|
||||
|
||||
def set_completer(self, search_data):
|
||||
search_data = search_data["data"]["Catalog"]["searchStore"]["elements"]
|
||||
titles = [i.get("title") for i in search_data]
|
||||
model = QStringListModel()
|
||||
model.setStringList(titles)
|
||||
self.completer.setModel(model)
|
||||
|
||||
def show_search_results(self):
|
||||
self.show_info.emit(self.search_bar.text())
|
|
@ -15,6 +15,7 @@ class Ui_RareSettings(object):
|
|||
def setupUi(self, RareSettings):
|
||||
RareSettings.setObjectName("RareSettings")
|
||||
RareSettings.resize(694, 532)
|
||||
|
||||
self.rare_layout = QtWidgets.QGridLayout(RareSettings)
|
||||
self.rare_layout.setObjectName("rare_layout")
|
||||
self.settings_group = QtWidgets.QGroupBox(RareSettings)
|
||||
|
@ -24,6 +25,13 @@ class Ui_RareSettings(object):
|
|||
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.confirm_start.setObjectName("confirm_start")
|
||||
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
|
||||
self.notification = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.notification.setObjectName("notification")
|
||||
self.behavior_layout.addWidget(self.notification, 4, 0, 1, 1)
|
||||
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.auto_update.setObjectName("auto_update")
|
||||
self.behavior_layout.addWidget(self.auto_update, 1, 0, 1, 1)
|
||||
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.behavior_layout.addItem(spacerItem, 2, 1, 2, 1)
|
||||
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
|
||||
|
@ -44,6 +52,15 @@ class Ui_RareSettings(object):
|
|||
self.log_games = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.log_games.setObjectName("log_games")
|
||||
self.behavior_layout.addWidget(self.log_games, 6, 0, 1, 1)
|
||||
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.confirm_start.setObjectName("confirm_start")
|
||||
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
|
||||
self.sys_tray = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.sys_tray.setObjectName("sys_tray")
|
||||
self.behavior_layout.addWidget(self.sys_tray, 0, 0, 1, 1)
|
||||
self.image_cache = QtWidgets.QCheckBox(self.settings_group)
|
||||
self.image_cache.setObjectName("image_cache")
|
||||
self.behavior_layout.addWidget(self.image_cache, 6, 0, 1, 1)
|
||||
self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop)
|
||||
self.interface_group = QtWidgets.QGroupBox(RareSettings)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
|
@ -121,6 +138,13 @@ class Ui_RareSettings(object):
|
|||
self.img_dir_layout = QtWidgets.QVBoxLayout(self.img_dir_group)
|
||||
self.img_dir_layout.setObjectName("img_dir_layout")
|
||||
self.rare_layout.addWidget(self.img_dir_group, 0, 0, 1, 1)
|
||||
self.label = QtWidgets.QLabel(self.img_dir_group)
|
||||
self.label.setWordWrap(True)
|
||||
self.label.setObjectName("label")
|
||||
self.img_dir_layout.addWidget(self.label)
|
||||
self.rare_layout.addWidget(self.img_dir_group, 0, 0, 1, 1, QtCore.Qt.AlignTop)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.rare_layout.addItem(spacerItem2, 3, 0, 1, 2)
|
||||
|
||||
self.retranslateUi(RareSettings)
|
||||
QtCore.QMetaObject.connectSlotsByName(RareSettings)
|
||||
|
@ -136,6 +160,16 @@ class Ui_RareSettings(object):
|
|||
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
|
||||
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
|
||||
self.log_games.setText(_translate("RareSettings", "Show console for game debug"))
|
||||
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
|
||||
self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
|
||||
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
|
||||
self.auto_sync_cloud.setText(_translate("RareSettings", "Automatically sync with cloud"))
|
||||
self.confirm_start.setText(_translate("RareSettings", "Confirm game launch"))
|
||||
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
|
||||
self.image_cache.setText(_translate("RareSettings", "Cache images in store"))
|
||||
self.log_dir_group.setTitle(_translate("RareSettings", "Logs"))
|
||||
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
|
||||
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
|
||||
self.interface_group.setTitle(_translate("RareSettings", "Interface"))
|
||||
self.style_label.setText(_translate("RareSettings", "Style Sheet"))
|
||||
self.color_select.setItemText(0, _translate("RareSettings", "None"))
|
||||
|
@ -150,6 +184,8 @@ class Ui_RareSettings(object):
|
|||
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
|
||||
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
|
||||
self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory"))
|
||||
self.label.setText(_translate("RareSettings",
|
||||
"To change image directory, edit XDG_DATA_HOME variable. To change cache directory edit XDG_CACHE_HOME variable"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RareSettings</class>
|
||||
<widget class="QWidget" name="RareSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>694</width>
|
||||
<height>532</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>RareSettings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="rare_layout">
|
||||
<item row="2" column="0" alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="settings_group">
|
||||
<property name="title">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="behavior_layout">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="confirm_start">
|
||||
<property name="text">
|
||||
<string>Confirm game launch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" rowspan="2">
|
||||
<spacer name="settings_hspacer">
|
||||
<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 row="1" column="0">
|
||||
<widget class="QCheckBox" name="auto_update">
|
||||
<property name="text">
|
||||
<string>Update games on application startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="save_size">
|
||||
<property name="text">
|
||||
<string>Restore window size on application startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="auto_sync_cloud">
|
||||
<property name="text">
|
||||
<string>Automatically sync with cloud</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="notification">
|
||||
<property name="text">
|
||||
<string>Show notification on download completion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="sys_tray">
|
||||
<property name="text">
|
||||
<string>Exit to System tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="log_games">
|
||||
<property name="text">
|
||||
<string>Show console for game debug</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="interface_group">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Interface</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="interface_layout">
|
||||
<item row="2" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="style_label">
|
||||
<property name="text">
|
||||
<string>Style Sheet</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="interface_hspacer">
|
||||
<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 row="0" column="1">
|
||||
<widget class="QComboBox" name="lang_select"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="color_select">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="style_select">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="lang_label">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" alignment="Qt::AlignRight">
|
||||
<widget class="QLabel" name="color_label">
|
||||
<property name="text">
|
||||
<string>Color Scheme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QLabel" name="interface_info">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restart Rare to apply.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Shortcuts</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="desktop_link">
|
||||
<property name="text">
|
||||
<string>Create Desktop link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="startmenu_link">
|
||||
<property name="text">
|
||||
<string>Create start menu link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="rpc_layout"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<spacer name="rare_vspacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="log_dir_group">
|
||||
<property name="title">
|
||||
<string>Logs</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="log_dir_layout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="log_dir_open_button">
|
||||
<property name="text">
|
||||
<string>Open Log directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="log_dir_clean_button">
|
||||
<property name="text">
|
||||
<string>Clean Log directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="log_dir_size_label">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="img_dir_group">
|
||||
<property name="title">
|
||||
<string>Image Cache Directory</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="img_dir_layout"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
0
rare/ui/components/tabs/store/__init__.py
Normal file
0
rare/ui/components/tabs/store/__init__.py
Normal file
136
rare/ui/components/tabs/store/browse_games.py
Normal file
136
rare/ui/components/tabs/store/browse_games.py
Normal 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_())
|
204
rare/ui/components/tabs/store/browse_games.ui
Normal file
204
rare/ui/components/tabs/store/browse_games.ui
Normal 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>
|
97
rare/ui/components/tabs/store/shop_game_info.py
Normal file
97
rare/ui/components/tabs/store/shop_game_info.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# -*- 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, 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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
self.open_store_button.setObjectName("open_store_button")
|
||||
self.verticalLayout_2.addWidget(self.open_store_button)
|
||||
self.wishlist_button = QtWidgets.QPushButton(shop_info)
|
||||
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)
|
||||
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"))
|
||||
|
||||
|
||||
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_())
|
155
rare/ui/components/tabs/store/shop_game_info.ui
Normal file
155
rare/ui/components/tabs/store/shop_game_info.ui
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?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="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<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="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="text">
|
||||
<string>Buy Game in Epic Games Store</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="wishlist_button">
|
||||
<property name="text">
|
||||
<string>Add to wishlist</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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>
|
||||
<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>
|
62
rare/ui/components/tabs/store/store.py
Normal file
62
rare/ui/components/tabs/store/store.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# -*- 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(697, 362)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(ShopWidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.scrollArea = QtWidgets.QScrollArea(ShopWidget)
|
||||
self.scrollArea.setWidgetResizable(True)
|
||||
self.scrollArea.setObjectName("scrollArea")
|
||||
self.scrollAreaWidgetContents = QtWidgets.QWidget()
|
||||
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 677, 342))
|
||||
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.free_game_group_box = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
|
||||
self.free_game_group_box.setObjectName("free_game_group_box")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.free_game_group_box)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.free_games_stack = QtWidgets.QStackedWidget(self.free_game_group_box)
|
||||
self.free_games_stack.setObjectName("free_games_stack")
|
||||
self.verticalLayout_2.addWidget(self.free_games_stack)
|
||||
self.verticalLayout_3.addWidget(self.free_game_group_box)
|
||||
self.wishlist_gb = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
|
||||
self.wishlist_gb.setObjectName("wishlist_gb")
|
||||
self.verticalLayout_3.addWidget(self.wishlist_gb)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_3.addItem(spacerItem)
|
||||
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
|
||||
self.verticalLayout.addWidget(self.scrollArea)
|
||||
|
||||
self.retranslateUi(ShopWidget)
|
||||
self.free_games_stack.setCurrentIndex(-1)
|
||||
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.wishlist_gb.setTitle(_translate("ShopWidget", "Discounts from your wishlist"))
|
||||
|
||||
|
||||
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_())
|
76
rare/ui/components/tabs/store/store.ui
Normal file
76
rare/ui/components/tabs/store/store.ui
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?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>697</width>
|
||||
<height>362</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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>677</width>
|
||||
<height>342</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="free_game_group_box">
|
||||
<property name="title">
|
||||
<string>Free Games</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="free_games_stack">
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="wishlist_gb">
|
||||
<property name="title">
|
||||
<string>Discounts from your wishlist</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer">
|
||||
<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>
|
|
@ -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
|
||||
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.ui.utils.pathedit import Ui_PathEdit
|
||||
from rare.utils.qt_requests import QtRequestManager
|
||||
|
||||
logger = getLogger("ExtraWidgets")
|
||||
|
||||
|
||||
class FlowLayout(QLayout):
|
||||
|
@ -114,13 +121,6 @@ class FlowLayout(QLayout):
|
|||
return parent.spacing()
|
||||
|
||||
|
||||
class ClickableLabel(QLabel):
|
||||
clicked = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
super(ClickableLabel, self).__init__()
|
||||
|
||||
|
||||
class PathEdit(QWidget, Ui_PathEdit):
|
||||
def __init__(self,
|
||||
text: str = "",
|
||||
|
@ -257,3 +257,78 @@ 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
|
||||
image: Image.Image = Image.open(io.BytesIO(data))
|
||||
image = image.resize((self.img_size[0], self.img_size[1]))
|
||||
|
||||
if QSettings().value("cache_images", True, 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
92
rare/utils/qt_requests.py
Normal 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
|
|
@ -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")
|
||||
|
@ -75,6 +77,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 = {}
|
||||
|
@ -106,6 +109,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:
|
||||
|
|
|
@ -24,6 +24,7 @@ logger = getLogger("Utils")
|
|||
s = QSettings("Rare", "Rare")
|
||||
|
||||
|
||||
|
||||
def download_images(signal: pyqtSignal, core: LegendaryCore):
|
||||
if not os.path.isdir(image_dir):
|
||||
os.makedirs(image_dir)
|
||||
|
@ -280,12 +281,12 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -
|
|||
igame = core.get_installed_game(app_name)
|
||||
|
||||
if os.path.exists(
|
||||
os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
|
||||
os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join(data_dir, 'images'), str),
|
||||
igame.app_name, 'Thumbnail.png')):
|
||||
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
|
||||
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join(data_dir, 'images'), str),
|
||||
igame.app_name, 'Thumbnail')
|
||||
else:
|
||||
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
|
||||
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join('images'), str),
|
||||
igame.app_name, 'DieselGameBoxTall')
|
||||
# Linux
|
||||
if platform.system() == "Linux":
|
||||
|
@ -329,7 +330,6 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -
|
|||
target = os.path.abspath(sys.argv[0])
|
||||
|
||||
# Name of link file
|
||||
|
||||
linkName = igame.title
|
||||
for c in r'<>?":|\/*':
|
||||
linkName.replace(c, "")
|
||||
|
@ -376,3 +376,4 @@ def get_uninstalled_pixmap(app_name: str) -> QPixmap:
|
|||
else:
|
||||
pixmap = QPixmap()
|
||||
return pixmap
|
||||
|
||||
|
|
Loading…
Reference in a new issue