diff --git a/custom_legendary/models/game.py b/custom_legendary/models/game.py
index 16e2d790..b2ea5682 100644
--- a/custom_legendary/models/game.py
+++ b/custom_legendary/models/game.py
@@ -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}}]}]}}
diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py
index fe0213f2..566e08bf 100644
--- a/rare/components/tab_widget.py
+++ b/rare/components/tab_widget.py
@@ -13,6 +13,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
@@ -22,7 +23,7 @@ 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))
@@ -38,6 +39,9 @@ class TabWidget(QTabWidget):
self.cloud_saves = SyncSaves(core, self)
self.addTab(self.cloud_saves, "Cloud Saves")
+ self.store = Shop()
+ self.addTab(self.store, self.tr("Store"))
+
self.settings = SettingsTab(core, self)
# Space Tab
@@ -92,9 +96,15 @@ 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()
+
def install_game(self, app_name, disable_path=False):
install_dialog = InstallDialog(self.core,
InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)),
diff --git a/rare/components/tabs/games/game_widgets/installed_icon_widget.py b/rare/components/tabs/games/game_widgets/installed_icon_widget.py
index e78e753b..a52e86f0 100644
--- a/rare/components/tabs/games/game_widgets/installed_icon_widget.py
+++ b/rare/components/tabs/games/game_widgets/installed_icon_widget.py
@@ -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)
diff --git a/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py b/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py
index afa5bbef..bdd8b14f 100644
--- a/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py
+++ b/rare/components/tabs/games/game_widgets/uninstalled_icon_widget.py
@@ -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)
diff --git a/rare/components/tabs/shop/__init__.py b/rare/components/tabs/shop/__init__.py
new file mode 100644
index 00000000..1b883bd7
--- /dev/null
+++ b/rare/components/tabs/shop/__init__.py
@@ -0,0 +1,28 @@
+from PyQt5.QtWidgets import QStackedWidget, QWidget, QVBoxLayout, QLabel
+
+from rare.components.tabs.shop.shop_info import ShopGameInfo
+from rare.components.tabs.shop.shop_widget import ShopWidget
+
+
+class Shop(QStackedWidget):
+ init = False
+ def __init__(self):
+ super(Shop, self).__init__()
+
+ self.shop = ShopWidget()
+ self.addWidget(self.shop)
+
+ self.info = ShopGameInfo()
+ self.addWidget(self.info)
+
+ self.shop.show_info.connect(self.show_info)
+
+ def load(self):
+ if not self.init:
+ self.init = True
+ self.shop.load()
+
+
+ def show_info(self, slug):
+ self.info.update_game(slug)
+ self.setCurrentIndex(1)
diff --git a/rare/components/tabs/shop/shop_info.py b/rare/components/tabs/shop/shop_info.py
new file mode 100644
index 00000000..a331a643
--- /dev/null
+++ b/rare/components/tabs/shop/shop_info.py
@@ -0,0 +1,35 @@
+import os
+import webbrowser
+
+from PyQt5.QtCore import QLocale, Qt
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtNetwork import QNetworkAccessManager
+from PyQt5.QtWidgets import QWidget
+
+from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info
+from rare.utils import api_utils
+
+
+class ShopGameInfo(QWidget, Ui_shop_info):
+ slug = ""
+
+ def __init__(self):
+ super(ShopGameInfo, self).__init__()
+ self.setupUi(self)
+ self.pushButton.clicked.connect(self.button_clicked)
+ self.manager = QNetworkAccessManager()
+
+ def update_game(self, slug: str):
+ locale = QLocale.system().name().split("_")[0]
+ game = api_utils.get_product(slug, locale)
+ self.slug = slug
+ self.title.setText(game[0]["productName"])
+ self.image.setPixmap(
+ QPixmap(os.path.expanduser(f"~/.cache/rare/cache/{game[0]['productName']}.png")).scaled(180,
+ int(180 * 9 / 16),
+ transformMode=Qt.SmoothTransformation))
+
+ self.dev.setText(game[0]["data"]["meta"]["developer"][0])
+
+ def button_clicked(self):
+ webbrowser.open("https://www.epicgames.com/store/de/p/" + self.slug)
diff --git a/rare/components/tabs/shop/shop_widget.py b/rare/components/tabs/shop/shop_widget.py
new file mode 100644
index 00000000..efc0ce18
--- /dev/null
+++ b/rare/components/tabs/shop/shop_widget.py
@@ -0,0 +1,209 @@
+import datetime
+import json
+import logging
+import os
+from json import JSONDecodeError
+
+import requests
+from PyQt5 import QtGui
+from PyQt5.QtCore import Qt, pyqtSignal, QUrl, QByteArray, QJsonDocument, QJsonParseError, QObjectCleanupHandler, \
+ QStringListModel
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout, QCompleter
+
+from rare.ui.components.tabs.store.store import Ui_ShopWidget
+from rare.utils.extra_widgets import WaitingSpinner
+from rare.utils.utils import get_lang
+
+
+class ShopWidget(QWidget, Ui_ShopWidget):
+ show_info = pyqtSignal(str)
+ free_game_widgets = []
+
+ def __init__(self):
+ super(ShopWidget, self).__init__()
+ self.setupUi(self)
+ self.search_results.setVisible(False)
+ self.manager = QNetworkAccessManager()
+ self.free_games_stack.addWidget(WaitingSpinner())
+ self.free_games_stack.setCurrentIndex(1)
+ self.search.textChanged.connect(self.search_games)
+ self.completer = QCompleter()
+ self.completer.setCaseSensitivity(Qt.CaseInsensitive)
+ self.search.setCompleter(self.completer)
+ self.search.returnPressed.connect(self.show_game)
+ self.data = []
+
+ def load(self):
+ if not os.path.exists(p := os.path.expanduser(f"~/.cache/rare/cache/")):
+ os.makedirs(p)
+ url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions"
+ self.free_game_request = self.manager.get(QNetworkRequest(QUrl(url)))
+ self.free_game_request.readyRead.connect(self.add_free_games)
+ self.free_game_request.finished.connect(self.free_game_request.deleteLater if self.free_game_request else None)
+
+ # free_games = api_utils.get_free_games()
+
+ def add_free_games(self):
+ if self.free_game_request:
+ if self.free_game_request.error() == QNetworkReply.NoError:
+ try:
+ free_games = json.loads(self.free_game_request.readAll().data().decode())
+ except JSONDecodeError:
+ print(self.free_game_request.readAll().data().decode())
+ return
+ else:
+ return
+ else:
+ return
+ free_games = free_games["data"]["Catalog"]["searchStore"]["elements"]
+ 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
+ end_date = datetime.datetime.strptime(
+ game["promotions"].get("promotionalOffers", game["promotions"].get("upcomingPromotionalOffers"))[0]["promotionalOffers"][0]["endDate"],
+ '%Y-%m-%dT%H:%M:%S.%fZ')
+ start_date = datetime.datetime.strptime(
+ game["promotions"].get("promotionalOffers", game["promotions"].get("upcomingPromotionalOffers"))[0][
+ "promotionalOffers"][0]["startDate"],
+ '%Y-%m-%dT%H:%M:%S.%fZ')
+ except IndexError:
+ print("index error")
+ continue
+ except KeyError:
+ print("keyerror")
+ 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(free_game)
+ w.show_info.connect(self.show_info)
+ self.free_game_now.layout().addWidget(w)
+ self.free_game_widgets.append(w)
+ self.free_game_group_box_2.setMinimumHeight(200)
+
+ for free_game in coming_free_games:
+ w = GameWidget(free_game)
+ if free_game["title"] != "Mystery Game":
+ w.show_info.connect(self.show_info)
+ self.comming_free_game.layout().addWidget(w)
+ self.free_game_widgets.append(w)
+ self.free_games_stack.setCurrentIndex(0)
+
+ def search_games(self, text):
+ if text == "":
+ self.search_results.setVisible(False)
+ else:
+ locale = get_lang()
+ payload = QByteArray()
+ payload.append(
+ "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")
+
+ payload = json.dumps({
+ "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",
+ "variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base", "count": 10,
+ "country": "DE", "keywords": text, "locale": locale, "sortDir": "DESC",
+ "allowCountries": locale.upper(),
+ "start": 0, "tag": "", "withMapping": False, "withPrice": True}
+ }).encode()
+ request = QNetworkRequest(QUrl("https://www.epicgames.com/graphql"))
+ request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
+ self.search_request = self.manager.post(request, payload)
+ # self.search_request = self.manager.post(QNetworkRequest(QUrl("https://www.epicgames.com/graphql")), payload)
+ self.search_request.readyRead.connect(self.show_search_results)
+ self.search_request.finished.connect(
+ self.search_request.deleteLater if self.search_request else None)
+
+ def show_search_results(self):
+ if self.search_request:
+ if self.search_request.error() == QNetworkReply.NoError:
+ error = QJsonParseError()
+ json_data = QJsonDocument.fromJson(self.search_request.readAll().data(), error)
+ if QJsonParseError.NoError == error.error:
+ data = json.loads(json_data.toJson().data().decode())["data"]["Catalog"]["searchStore"]["elements"]
+ else:
+ logging.error(error.errorString())
+ self.search_results.setVisible(False)
+ return
+ #response = .decode(encoding="utf-8")
+ #print(response)
+ #results = json.loads(response)
+ else:
+ return
+ else:
+ return
+ self.data = data
+ titles = [i.get("title") for i in data]
+ model = QStringListModel()
+ model.setStringList(titles)
+ self.completer.setModel(model)
+ self.completer.popup()
+ # self.search_results.setLayout(layout)
+ # self.search_results.setVisible(True)
+
+ def show_game(self):
+ if self.data:
+ slug = self.data[0].get("productSlug")
+ self.show_info.emit(slug)
+
+
+class SearchResultItem(QWidget):
+ def __init__(self, json_info):
+ super(SearchResultItem, self).__init__()
+ self.layout = QHBoxLayout()
+ self.title = QLabel(json_info.get("title", "undefined"))
+ self.layout.addWidget(self.title)
+ self.slug = json_info.get("productSlug", "undefined")
+
+ self.setLayout(self.layout)
+
+
+class GameWidget(QWidget):
+ show_info = pyqtSignal(str)
+
+ def __init__(self, json_info):
+ super(GameWidget, self).__init__()
+ self.layout = QVBoxLayout()
+ self.image = QLabel()
+ self.slug = json_info["productSlug"]
+ if not os.path.exists(path := os.path.expanduser(f"~/.cache/rare/cache/{json_info['title']}.png")):
+ for img in json_info["keyImages"]:
+ if json_info["title"] != "Mystery Game":
+ if img["type"] == "DieselStoreFrontWide":
+ with open(path, "wb") as img_file:
+ content = requests.get(img["url"]).content
+ img_file.write(content)
+ break
+ else:
+ if img["type"] == "VaultClosed":
+ with open(path, "wb") as img_file:
+ content = requests.get(img["url"]).content
+ img_file.write(content)
+ break
+ else:
+ print("No image found")
+ width = 300
+ self.image.setPixmap(QPixmap(os.path.expanduser(f"~/.cache/rare/cache/{json_info['title']}.png"))
+ .scaled(width, int(width * 9 / 16), transformMode=Qt.SmoothTransformation))
+ self.layout.addWidget(self.image)
+
+ self.title = QLabel(json_info["title"])
+ self.layout.addWidget(self.title)
+
+ self.setLayout(self.layout)
+
+ def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
+ self.show_info.emit(self.slug)
diff --git a/rare/ui/components/tabs/store/shop_game_info.py b/rare/ui/components/tabs/store/shop_game_info.py
new file mode 100644
index 00000000..d0acab66
--- /dev/null
+++ b/rare/ui/components/tabs/store/shop_game_info.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'shop_game_info.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.4
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_shop_info(object):
+ def setupUi(self, shop_info):
+ shop_info.setObjectName("shop_info")
+ shop_info.resize(702, 468)
+ self.gridLayout = QtWidgets.QGridLayout(shop_info)
+ self.gridLayout.setObjectName("gridLayout")
+ self.pushButton = QtWidgets.QPushButton(shop_info)
+ self.pushButton.setObjectName("pushButton")
+ self.gridLayout.addWidget(self.pushButton, 3, 1, 1, 1)
+ self.price = QtWidgets.QLabel(shop_info)
+ self.price.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
+ self.price.setObjectName("price")
+ self.gridLayout.addWidget(self.price, 1, 1, 1, 1)
+ self.title = QtWidgets.QLabel(shop_info)
+ self.title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
+ self.title.setObjectName("title")
+ self.gridLayout.addWidget(self.title, 0, 1, 1, 1)
+ self.image = QtWidgets.QLabel(shop_info)
+ self.image.setObjectName("image")
+ self.gridLayout.addWidget(self.image, 0, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 4, 1, 1, 1)
+ self.dev = QtWidgets.QLabel(shop_info)
+ self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
+ self.dev.setObjectName("dev")
+ self.gridLayout.addWidget(self.dev, 2, 1, 1, 1)
+
+ self.retranslateUi(shop_info)
+ QtCore.QMetaObject.connectSlotsByName(shop_info)
+
+ def retranslateUi(self, shop_info):
+ _translate = QtCore.QCoreApplication.translate
+ shop_info.setWindowTitle(_translate("shop_info", "Form"))
+ self.pushButton.setText(_translate("shop_info", "Buy Game in Epic Games Store"))
+ self.price.setText(_translate("shop_info", "TextLabel"))
+ self.title.setText(_translate("shop_info", "Error"))
+ self.image.setText(_translate("shop_info", "TextLabel"))
+ self.dev.setText(_translate("shop_info", "TextLabel"))
+
+
+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_())
diff --git a/rare/ui/components/tabs/store/shop_game_info.ui b/rare/ui/components/tabs/store/shop_game_info.ui
new file mode 100644
index 00000000..c3235210
--- /dev/null
+++ b/rare/ui/components/tabs/store/shop_game_info.ui
@@ -0,0 +1,78 @@
+
+
+ shop_info
+
+
+
+ 0
+ 0
+ 702
+ 468
+
+
+
+ Form
+
+
+ -
+
+
+ Buy Game in Epic Games Store
+
+
+
+ -
+
+
+ TextLabel
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+ Error
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ TextLabel
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+
diff --git a/rare/ui/components/tabs/store/store.py b/rare/ui/components/tabs/store/store.py
new file mode 100644
index 00000000..ce8adcfb
--- /dev/null
+++ b/rare/ui/components/tabs/store/store.py
@@ -0,0 +1,80 @@
+# -*- 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, QtGui, 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.search = QtWidgets.QLineEdit(ShopWidget)
+ self.search.setObjectName("search")
+ self.verticalLayout.addWidget(self.search)
+ self.search_results = QtWidgets.QGroupBox(ShopWidget)
+ self.search_results.setFlat(False)
+ self.search_results.setObjectName("search_results")
+ self.verticalLayout.addWidget(self.search_results)
+ self.free_game_group_box_2 = QtWidgets.QGroupBox(ShopWidget)
+ self.free_game_group_box_2.setObjectName("free_game_group_box_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.free_game_group_box_2)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.free_games_stack = QtWidgets.QStackedWidget(self.free_game_group_box_2)
+ self.free_games_stack.setObjectName("free_games_stack")
+ self.free_games_page = QtWidgets.QWidget()
+ self.free_games_page.setObjectName("free_games_page")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.free_games_page)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.free_game_now = QtWidgets.QGroupBox(self.free_games_page)
+ self.free_game_now.setObjectName("free_game_now")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.free_game_now)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.horizontalLayout_3.addWidget(self.free_game_now)
+ self.comming_free_game = QtWidgets.QGroupBox(self.free_games_page)
+ self.comming_free_game.setObjectName("comming_free_game")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.comming_free_game)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.horizontalLayout_3.addWidget(self.comming_free_game)
+ self.free_games_stack.addWidget(self.free_games_page)
+ self.verticalLayout_2.addWidget(self.free_games_stack)
+ self.verticalLayout.addWidget(self.free_game_group_box_2)
+ self.games_groupbox_2 = QtWidgets.QGroupBox(ShopWidget)
+ self.games_groupbox_2.setObjectName("games_groupbox_2")
+ self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.games_groupbox_2)
+ self.horizontalLayout_7.setObjectName("horizontalLayout_7")
+ self.verticalLayout.addWidget(self.games_groupbox_2)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+
+ self.retranslateUi(ShopWidget)
+ self.free_games_stack.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(ShopWidget)
+
+ def retranslateUi(self, ShopWidget):
+ _translate = QtCore.QCoreApplication.translate
+ ShopWidget.setWindowTitle(_translate("ShopWidget", "Form"))
+ self.search.setPlaceholderText(_translate("ShopWidget", "Search Games"))
+ self.search_results.setTitle(_translate("ShopWidget", "Search results"))
+ self.free_game_group_box_2.setTitle(_translate("ShopWidget", "Free Games"))
+ self.free_game_now.setTitle(_translate("ShopWidget", "Free Game"))
+ self.comming_free_game.setTitle(_translate("ShopWidget", "Comming Free Game"))
+ self.games_groupbox_2.setTitle(_translate("ShopWidget", "Other nice games"))
+
+
+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_())
diff --git a/rare/ui/components/tabs/store/store.ui b/rare/ui/components/tabs/store/store.ui
new file mode 100644
index 00000000..1e470192
--- /dev/null
+++ b/rare/ui/components/tabs/store/store.ui
@@ -0,0 +1,95 @@
+
+
+ ShopWidget
+
+
+
+ 0
+ 0
+ 697
+ 362
+
+
+
+ Form
+
+
+ -
+
+
+ Search Games
+
+
+
+ -
+
+
+ Search results
+
+
+ false
+
+
+
+ -
+
+
+ Free Games
+
+
+
-
+
+
+ 0
+
+
+
+
-
+
+
+ Free Game
+
+
+
+
+ -
+
+
+ Comming Free Game
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Other nice games
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/rare/utils/extra_widgets.py b/rare/utils/extra_widgets.py
index 121ab59d..cfc66800 100644
--- a/rare/utils/extra_widgets.py
+++ b/rare/utils/extra_widgets.py
@@ -114,13 +114,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 = "",
diff --git a/rare/utils/models.py b/rare/utils/models.py
index 44400ddc..790868d1 100644
--- a/rare/utils/models.py
+++ b/rare/utils/models.py
@@ -34,3 +34,51 @@ class InstallQueueItemModel:
def __bool__(self):
return (self.status_q is not None) and (self.download is not None) and (self.options is not None)
+
+
+class ShopGame:
+ # TODO: Copyrights etc
+ def __init__(self, title: str = "", image_urls: dict = None, social_links: dict = None,
+ langs: list = None, reqs: list = None, publisher: str = "", developer: 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 # {"Betriebssystem":win7, processor:i9 9900k, ram...}; Note: name from language
+ self.publisher = publisher
+ self.developer = developer
+
+ @classmethod
+ def from_json(cls, data: dict):
+ if isinstance(data, list):
+ for product in data:
+ if product["_title"] == "home":
+ data = product
+ break
+ tmp = cls()
+ tmp.title = data.get("productName", "undefined")
+ tmp.img_urls = {
+ "DieselImage": data["data"]["about"]["image"]["src"],
+ "banner": data["data"]["hero"]["backgroundImageUrl"]
+ }
+ links = 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 = data["data"]["requirements"]["languages"]
+ tmp.reqs = {}
+ for system in data["data"]["requirements"]["systems"]:
+ tmp.reqs[system] = {}
+ for i in system:
+ tmp.reqs[i[system]["title"]] = tuple((i[system]["minimum"], i[system]["recommend"]))
+
+ tmp.publisher = data["data"]["meta"].get("publisher", "undefined")
+ tmp.developer = data["data"]["meta"].get("developer", "undefined")
+ return tmp
diff --git a/test.py b/test.py
new file mode 100644
index 00000000..de073353
--- /dev/null
+++ b/test.py
@@ -0,0 +1,20 @@
+import sys
+from PyQt5.QtCore import Qt, QStringListModel
+from PyQt5.QtWidgets import QApplication, QCompleter, QLineEdit
+
+def get_data(model):
+ model.setStringList(["completion", "data", "goes", "here"])
+
+if __name__ == "__main__":
+
+ app = QApplication(sys.argv)
+ edit = QLineEdit()
+ completer = QCompleter()
+ edit.setCompleter(completer)
+
+ model = QStringListModel()
+ completer.setModel(model)
+ get_data(model)
+
+ edit.show()
+ sys.exit(app.exec_())
\ No newline at end of file