From ce65ff30997683f3009300feb7c18722297046d3 Mon Sep 17 00:00:00 2001 From: ChemicalXandco <32775248+ChemicalXandco@users.noreply.github.com> Date: Fri, 21 May 2021 14:01:18 +0100 Subject: [PATCH 01/22] Update windows installer build to use python 3.9.5 --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 643f779e..ad131667 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,8 +52,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: '3.8' - - name: Install python deps + python-version: '3.9.5' + - name: Install python deps run: | pip3 install cx_Freeze setuptools wheel pip3 install -r requirements.txt From 6686a5ddc68f3e9f57823d6d1d1cada9cd64212d Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Fri, 21 May 2021 23:45:33 +0300 Subject: [PATCH 02/22] Simplify color scheme format `.scheme` files now should hold only the Active palette and for the Inactive and Disabled palettes only whatever colors differ. Add a few more color schemes based on popular themes. --- rare/styles/colors/Adapta-Nokto.scheme | 25 ++++++++ rare/styles/colors/Airy.scheme | 39 ++++++++++++ rare/styles/colors/Arc-Dark.scheme | 26 ++++++++ rare/styles/colors/Arc-Darker.scheme | 26 ++++++++ rare/styles/colors/Darker.scheme | 25 ++++++++ rare/styles/colors/Dusk.scheme | 26 ++++++++ rare/styles/colors/Numix-Dark.scheme | 84 ++++++++------------------ rare/styles/colors/Rare.scheme | 84 ++++++++------------------ rare/styles/colors/Zukitre.scheme | 25 ++++++++ rare/styles/colors/ia_ora.scheme | 61 ------------------- rare/utils/utils.py | 17 +++--- 11 files changed, 248 insertions(+), 190 deletions(-) create mode 100644 rare/styles/colors/Adapta-Nokto.scheme create mode 100644 rare/styles/colors/Airy.scheme create mode 100644 rare/styles/colors/Arc-Dark.scheme create mode 100644 rare/styles/colors/Arc-Darker.scheme create mode 100644 rare/styles/colors/Darker.scheme create mode 100644 rare/styles/colors/Dusk.scheme create mode 100644 rare/styles/colors/Zukitre.scheme delete mode 100644 rare/styles/colors/ia_ora.scheme diff --git a/rare/styles/colors/Adapta-Nokto.scheme b/rare/styles/colors/Adapta-Nokto.scheme new file mode 100644 index 00000000..0c2b5543 --- /dev/null +++ b/rare/styles/colors/Adapta-Nokto.scheme @@ -0,0 +1,25 @@ +[ColorScheme] +Active\AlternateBase=#ff273339 +Active\Base=#ff2f3d44 +Active\BrightText=#ffffffff +Active\Button=#ff263238 +Active\ButtonText=#ffb9c3c7 +Active\Dark=#ff1f292e +Active\Highlight=#ff00bcd4 +Active\HighlightedText=#ffffffff +Active\Light=#ff2f3e46 +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffb8b5b2 +Active\Midlight=#ffcbc7c4 +Active\PlaceholderText=#80b9c3c7 +Active\Shadow=#ff1d262b +Active\Text=#ffb9c3c7 +Active\ToolTipBase=#ff263238 +Active\ToolTipText=#ffcfd8dc +Active\Window=#ff263238 +Active\WindowText=#ffb9c3c7 +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\WindowText=#ff808080 diff --git a/rare/styles/colors/Airy.scheme b/rare/styles/colors/Airy.scheme new file mode 100644 index 00000000..f445a80a --- /dev/null +++ b/rare/styles/colors/Airy.scheme @@ -0,0 +1,39 @@ +[ColorScheme] +Active\AlternateBase=#ff5c5b5a +Active\Base=#ffc8c8c8 +Active\BrightText=#ff0a0a0a +Active\Button=#ffdcdcdc +Active\ButtonText=#ff0a0a0a +Active\Dark=#ff646464 +Active\Highlight=#ff0986d3 +Active\HighlightedText=#ff0a0a0a +Active\Light=#ffdcdcdc +Active\Link=#ff0986d3 +Active\LinkVisited=#ffa70b06 +Active\Mid=#ffe1e1e1 +Active\Midlight=#ff5e5c5b +Active\PlaceholderText=#80000000 +Active\Shadow=#ffe7e4e0 +Active\Text=#ff000000 +Active\ToolTipBase=#ff646464 +Active\ToolTipText=#ff050505 +Active\Window=#ffffffff +Active\WindowText=#ff000000 +Disabled\Base=#ff969696 +Disabled\BrightText=#ffffffff +Disabled\Button=#ff424245 +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\ToolTipText=#ffffffff +Disabled\Window=#ffc8c8c8 +Disabled\WindowText=#ffffffff +Inactive\Base=#ff969696 +Inactive\BrightText=#ff323232 +Inactive\Button=#ffb4b4b4 +Inactive\ButtonText=#ff323232 +Inactive\HighlightedText=#ff323232 +Inactive\Text=#ff323232 +Inactive\ToolTipText=#ff323232 +Inactive\Window=#ffc8c8c8 +Inactive\WindowText=#ff323232 diff --git a/rare/styles/colors/Arc-Dark.scheme b/rare/styles/colors/Arc-Dark.scheme new file mode 100644 index 00000000..c734d720 --- /dev/null +++ b/rare/styles/colors/Arc-Dark.scheme @@ -0,0 +1,26 @@ +[ColorScheme] +Active\AlternateBase=#ff3f4350 +Active\Base=#ff404552 +Active\BrightText=#ffffffff +Active\Button=#ff383c4a +Active\ButtonText=#ffd3dae3 +Active\Dark=#ff2e323d +Active\Highlight=#ff5294e2 +Active\HighlightedText=#ffffffff +Active\Light=#ff464b5c +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffa0a0a4 +Active\Midlight=#ffe9e7e3 +Active\PlaceholderText=#80d3dae3 +Active\Shadow=#ff2b2e39 +Active\Text=#ffd3dae3 +Active\ToolTipBase=#ffffffdc +Active\ToolTipText=#ffd3dae3 +Active\Window=#ff383c4a +Active\WindowText=#ffd3dae3 +Disabled\ButtonText=#ff858b96 +Disabled\Highlight=#ffe2e2e2 +Disabled\Text=#ff858b96 +Disabled\WindowText=#ff858b96 +Inactive\Highlight=#ff4084d6 diff --git a/rare/styles/colors/Arc-Darker.scheme b/rare/styles/colors/Arc-Darker.scheme new file mode 100644 index 00000000..0462ed08 --- /dev/null +++ b/rare/styles/colors/Arc-Darker.scheme @@ -0,0 +1,26 @@ +[ColorScheme] +Active\AlternateBase=#fffafafa +Active\Base=#ffffffff +Active\BrightText=#ffffffff +Active\Button=#fff5f6f7 +Active\ButtonText=#ff5c616c +Active\Dark=#ffcccdce +Active\Highlight=#ff5294e2 +Active\HighlightedText=#ffffffff +Active\Light=#ffffffff +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffa0a0a4 +Active\Midlight=#ffe9e7e3 +Active\PlaceholderText=#805c616c +Active\Shadow=#ffbdbdbe +Active\Text=#ff5c616c +Active\ToolTipBase=#ffffffdc +Active\ToolTipText=#ff5c616c +Active\Window=#fff5f6f7 +Active\WindowText=#ff5c616c +Disabled\ButtonText=#ffa8abb1 +Disabled\Highlight=#ffe2e2e2 +Disabled\Text=#ffa8abb1 +Disabled\WindowText=#ffa8abb1 +Inactive\Highlight=#ff4084d6 diff --git a/rare/styles/colors/Darker.scheme b/rare/styles/colors/Darker.scheme new file mode 100644 index 00000000..db91755f --- /dev/null +++ b/rare/styles/colors/Darker.scheme @@ -0,0 +1,25 @@ +[ColorScheme] +Active\AlternateBase=#ff5c5b5a +Active\Base=#ff3d3d3d +Active\BrightText=#ffffffff +Active\Button=#ff424245 +Active\ButtonText=#ffffffff +Active\Dark=#ff302f2e +Active\Highlight=#ff12608a +Active\HighlightedText=#fff9f9f9 +Active\Light=#ff979797 +Active\Link=#ff0986d3 +Active\LinkVisited=#ffa70b06 +Active\Mid=#ff4a4947 +Active\Midlight=#ff5e5c5b +Active\PlaceholderText=#80ffffff +Active\Shadow=#ffe7e4e0 +Active\Text=#ffffffff +Active\ToolTipBase=#ff3f3f36 +Active\ToolTipText=#ffffffff +Active\Window=#ff222020 +Active\WindowText=#ffffffff +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\WindowText=#ff808080 diff --git a/rare/styles/colors/Dusk.scheme b/rare/styles/colors/Dusk.scheme new file mode 100644 index 00000000..49e4012f --- /dev/null +++ b/rare/styles/colors/Dusk.scheme @@ -0,0 +1,26 @@ +[ColorScheme] +Active\AlternateBase=#ff7f7f7f +Active\Base=#ff7f7f7f +Active\BrightText=#ffffffff +Active\Button=#ff7f7f7f +Active\ButtonText=#ff000000 +Active\Dark=#ff7f7f7f +Active\Highlight=#ff308cc6 +Active\HighlightedText=#ffffffff +Active\Light=#ffffffff +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffb8b5b2 +Active\Midlight=#ffcbc7c4 +Active\PlaceholderText=#80000000 +Active\Shadow=#ff707070 +Active\Text=#ff000000 +Active\ToolTipBase=#ff7f7f7f +Active\ToolTipText=#ff000000 +Active\Window=#ff7f7f7f +Active\WindowText=#ff000000 +Disabled\ButtonText=#ffbebebe +Disabled\Highlight=#ff7f7f7f +Disabled\Shadow=#ffb1aeab +Disabled\Text=#ffbebebe +Disabled\WindowText=#ffbebebe diff --git a/rare/styles/colors/Numix-Dark.scheme b/rare/styles/colors/Numix-Dark.scheme index 7aa182da..faab227e 100644 --- a/rare/styles/colors/Numix-Dark.scheme +++ b/rare/styles/colors/Numix-Dark.scheme @@ -1,61 +1,25 @@ [ColorScheme] -Active\AlternateBase=#2f2f2f -Active\Base=#333333 -Active\BrightText=#ffffff -Active\Button=#525252 -Active\ButtonText=#dddddd -Active\Dark=#333333 -Active\Highlight=#f0544c -Active\HighlightedText=#ffffff -Active\Light=#555555 -Active\Link=#fc6f5d -Active\LinkVisited=#853931 -Active\Mid=#a0a0a4 -Active\Midlight=#e9e7e3 -Active\PlaceholderText=#eeeeee -Active\Shadow=#343434 -Active\Text=#eeeeee -Active\ToolTipBase=#444444 -Active\ToolTipText=#eeeeee -Active\Window=#444444 -Active\WindowText=#dddddd -Disabled\AlternateBase=#2f2f2f -Disabled\Base=#333333 -Disabled\BrightText=#ffffff -Disabled\Button=#525252 -Disabled\ButtonText=#808080 -Disabled\Dark=#333333 -Disabled\Highlight=#f0544c -Disabled\HighlightedText=#808080 -Disabled\Light=#555555 -Disabled\Link=#fc6f5d -Disabled\LinkVisited=#853931 -Disabled\Mid=#a0a0a4 -Disabled\Midlight=#e9e7e3 -Disabled\PlaceholderText=#eeeeee -Disabled\Shadow=#343434 -Disabled\Text=#808080 -Disabled\ToolTipBase=#444444 -Disabled\ToolTipText=#eeeeee -Disabled\Window=#444444 -Disabled\WindowText=#808080 -Inactive\AlternateBase=#2f2f2f -Inactive\Base=#333333 -Inactive\BrightText=#ffffff -Inactive\Button=#525252 -Inactive\ButtonText=#dddddd -Inactive\Dark=#333333 -Inactive\Highlight=#f0544c -Inactive\HighlightedText=#ffffff -Inactive\Light=#555555 -Inactive\Link=#fc6f5d -Inactive\LinkVisited=#853931 -Inactive\Mid=#a0a0a4 -Inactive\Midlight=#e9e7e3 -Inactive\PlaceholderText=#eeeeee -Inactive\Shadow=#343434 -Inactive\Text=#eeeeee -Inactive\ToolTipBase=#444444 -Inactive\ToolTipText=#eeeeee -Inactive\Window=#444444 -Inactive\WindowText=#dddddd +Active\AlternateBase=#ff2f2f2f +Active\Base=#ff333333 +Active\BrightText=#ffffffff +Active\Button=#ff525252 +Active\ButtonText=#ffdddddd +Active\Dark=#ff333333 +Active\Highlight=#fff0544c +Active\HighlightedText=#ffffffff +Active\Light=#ff555555 +Active\Link=#fffc6f5d +Active\LinkVisited=#ff853931 +Active\Mid=#ffa0a0a4 +Active\Midlight=#ffe9e7e3 +Active\PlaceholderText=#80eeeeee +Active\Shadow=#ff343434 +Active\Text=#ffeeeeee +Active\ToolTipBase=#ff444444 +Active\ToolTipText=#ffeeeeee +Active\Window=#ff444444 +Active\WindowText=#ffdddddd +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\WindowText=#ff808080 diff --git a/rare/styles/colors/Rare.scheme b/rare/styles/colors/Rare.scheme index 1b954210..e2f3146c 100644 --- a/rare/styles/colors/Rare.scheme +++ b/rare/styles/colors/Rare.scheme @@ -1,61 +1,25 @@ [ColorScheme] -Active\AlternateBase=#f7f7f7 -Active\Base=#333344 -Active\BrightText=#ffffff -Active\Button=#3c3f41 -Active\ButtonText=#eeeeee -Active\Dark=#9f0910 -Active\Highlight=#2f4f4f -Active\HighlightedText=#eeeeee -Active\Light=#ffffff -Active\Link=#0000ff -Active\LinkVisited=#ff00ff -Active\Mid=#b80e35 -Active\Midlight=#ca0651 -Active\PlaceholderText=#eeeeee -Active\Shadow=#767676 -Active\Text=#eeeeee -Active\ToolTipBase=#ffffdc -Active\ToolTipText=#eeeeee -Active\Window=#202225 -Active\WindowText=#eeeeee -Disabled\AlternateBase=#f7f7f7 -Disabled\Base=#333344 -Disabled\BrightText=#ffffff -Disabled\Button=#3c3f41 -Disabled\ButtonText=#808080 -Disabled\Dark=#9f0910 -Disabled\Highlight=#2f4f4f -Disabled\HighlightedText=#808080 -Disabled\Light=#ffffff -Disabled\Link=#0000ff -Disabled\LinkVisited=#ff00ff -Disabled\Mid=#b80e35 -Disabled\Midlight=#ca0651 -Disabled\PlaceholderText=#eeeeee -Disabled\Shadow=#767676 -Disabled\Text=#808080 -Disabled\ToolTipBase=#ffffdc -Disabled\ToolTipText=#eeeeee -Disabled\Window=#202225 -Disabled\WindowText=#808080 -Inactive\AlternateBase=#f7f7f7 -Inactive\Base=#333344 -Inactive\BrightText=#ffffff -Inactive\Button=#3c3f41 -Inactive\ButtonText=#eeeeee -Inactive\Dark=#9f0910 -Inactive\Highlight=#2f4f4f -Inactive\HighlightedText=#eeeeee -Inactive\Light=#ffffff -Inactive\Link=#0000ff -Inactive\LinkVisited=#ff00ff -Inactive\Mid=#b80e35 -Inactive\Midlight=#ca0651 -Inactive\PlaceholderText=#eeeeee -Inactive\Shadow=#767676 -Inactive\Text=#eeeeee -Inactive\ToolTipBase=#ffffdc -Inactive\ToolTipText=#eeeeee -Inactive\Window=#202225 -Inactive\WindowText=#eeeeee +Active\AlternateBase=#fff7f7f7 +Active\Base=#ff333344 +Active\BrightText=#ffffffff +Active\Button=#ff3c3f41 +Active\ButtonText=#ffeeeeee +Active\Dark=#ff9f0910 +Active\Highlight=#ff2f4f4f +Active\HighlightedText=#ffeeeeee +Active\Light=#ffffffff +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffb80e35 +Active\Midlight=#ffca0651 +Active\PlaceholderText=#80eeeeee +Active\Shadow=#ff767676 +Active\Text=#ffeeeeee +Active\ToolTipBase=#ffffffdc +Active\ToolTipText=#ffeeeeee +Active\Window=#ff202225 +Active\WindowText=#ffeeeeee +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\WindowText=#ff808080 diff --git a/rare/styles/colors/Zukitre.scheme b/rare/styles/colors/Zukitre.scheme new file mode 100644 index 00000000..85fd63bc --- /dev/null +++ b/rare/styles/colors/Zukitre.scheme @@ -0,0 +1,25 @@ +[ColorScheme] +Active\AlternateBase=#fff7f7f7 +Active\Base=#fff7f7f7 +Active\BrightText=#ffffffff +Active\Button=#ffd6d6d6 +Active\ButtonText=#ff2c2c2c +Active\Dark=#ffb3b3b3 +Active\Highlight=#ff258ef8 +Active\HighlightedText=#ffffffff +Active\Light=#ffffffff +Active\Link=#ff0000ff +Active\LinkVisited=#ffff00ff +Active\Mid=#ffa0a0a4 +Active\Midlight=#ffe9e7e3 +Active\PlaceholderText=#802c2c2c +Active\Shadow=#ffa5a5a5 +Active\Text=#ff2c2c2c +Active\ToolTipBase=#ffffffdc +Active\ToolTipText=#ff000000 +Active\Window=#ffd6d6d6 +Active\WindowText=#ff2c2c2c +Disabled\ButtonText=#ff808080 +Disabled\HighlightedText=#ff808080 +Disabled\Text=#ff808080 +Disabled\WindowText=#ff808080 diff --git a/rare/styles/colors/ia_ora.scheme b/rare/styles/colors/ia_ora.scheme deleted file mode 100644 index 5fbbc868..00000000 --- a/rare/styles/colors/ia_ora.scheme +++ /dev/null @@ -1,61 +0,0 @@ -[ColorScheme] -Active\AlternateBase=#eff3f7 -Active\Base=#eff3f7 -Active\BrightText=#ffffff -Active\Button=#eff3f7 -Active\ButtonText=#000000 -Active\Dark=#c7cbce -Active\Highlight=#4965ae -Active\HighlightedText=#ffffff -Active\Light=#ffffff -Active\Link=#0000ff -Active\LinkVisited=#ff00ff -Active\Mid=#a0a0a4 -Active\Midlight=#e9e7e3 -Active\PlaceholderText=#000000 -Active\Shadow=#b8bbbe -Active\Text=#000000 -Active\ToolTipBase=#ffffdc -Active\ToolTipText=#000000 -Active\Window=#eff3f7 -Active\WindowText=#000000 -Disabled\AlternateBase=#eff3f7 -Disabled\Base=#eff3f7 -Disabled\BrightText=#ffffff -Disabled\Button=#eff3f7 -Disabled\ButtonText=#808080 -Disabled\Dark=#c7cbce -Disabled\Highlight=#4965ae -Disabled\HighlightedText=#808080 -Disabled\Light=#ffffff -Disabled\Link=#0000ff -Disabled\LinkVisited=#ff00ff -Disabled\Mid=#a0a0a4 -Disabled\Midlight=#e9e7e3 -Disabled\PlaceholderText=#000000 -Disabled\Shadow=#b8bbbe -Disabled\Text=#808080 -Disabled\ToolTipBase=#ffffdc -Disabled\ToolTipText=#000000 -Disabled\Window=#eff3f7 -Disabled\WindowText=#808080 -Inactive\AlternateBase=#eff3f7 -Inactive\Base=#eff3f7 -Inactive\BrightText=#ffffff -Inactive\Button=#eff3f7 -Inactive\ButtonText=#000000 -Inactive\Dark=#c7cbce -Inactive\Highlight=#4965ae -Inactive\HighlightedText=#ffffff -Inactive\Light=#ffffff -Inactive\Link=#0000ff -Inactive\LinkVisited=#ff00ff -Inactive\Mid=#a0a0a4 -Inactive\Midlight=#e9e7e3 -Inactive\PlaceholderText=#000000 -Inactive\Shadow=#b8bbbe -Inactive\Text=#000000 -Inactive\ToolTipBase=#ffffdc -Inactive\ToolTipText=#000000 -Inactive\Window=#eff3f7 -Inactive\WindowText=#000000 diff --git a/rare/utils/utils.py b/rare/utils/utils.py index c09c4f1a..ec18e899 100644 --- a/rare/utils/utils.py +++ b/rare/utils/utils.py @@ -157,7 +157,7 @@ color_group_map = { def load_color_scheme(path: str): - custom_palette = QPalette() + palette = QPalette() settings = QSettings(path, QSettings.IniFormat) try: settings.beginGroup("ColorScheme") @@ -166,17 +166,16 @@ def load_color_scheme(path: str): group = QPalette.ColorGroup(g) for r in color_role_map: role = QPalette.ColorRole(r) - custom_palette.setColor(group, role, QColor(settings.value(color_role_map[r]))) + color = settings.value(color_role_map[r], None) + if color is not None: + palette.setColor(group, role, QColor(color)) + else: + palette.setColor(group, role, palette.color(QPalette.Active, role)) settings.endGroup() settings.endGroup() - text_color = custom_palette.text().color() - text_color.setAlpha(128) - custom_palette.setColor(custom_palette.Active, custom_palette.PlaceholderText, text_color) - custom_palette.setColor(custom_palette.Inactive, custom_palette.PlaceholderText, text_color) - custom_palette.setColor(custom_palette.Disabled, custom_palette.PlaceholderText, text_color) except: - custom_palette = None - return custom_palette + palette = None + return palette def get_color_schemes(): From 1ef5ad0d612350bd480e0eaacb8bcc280f5033cc Mon Sep 17 00:00:00 2001 From: Dummerle Date: Sat, 22 May 2021 17:49:01 +0200 Subject: [PATCH 03/22] Add two options to create desktop and startmenu link --- rare/__main__.py | 16 ++++++++++++++- rare/utils/utils.py | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/rare/__main__.py b/rare/__main__.py index cbb387f8..271ddaff 100644 --- a/rare/__main__.py +++ b/rare/__main__.py @@ -1,8 +1,10 @@ +#!/usr/bin/python + import os from argparse import ArgumentParser from rare import __version__ -from rare.utils import singleton +from rare.utils import singleton, utils def main(): @@ -16,6 +18,11 @@ def main(): help="Do not download and check data from ProtonDB. Disable it, if you don't need grades") parser.add_argument("--enable-protondb", action="store_true", dest="enable_protondb", help="Enable ProtonDB data, after disabled") + + parser.add_argument("--desktop-shortcut", action="store_true", dest="desktop_shortcut", + help="Use this, if there is no link on desktop to start Rare") + parser.add_argument("--startmenu-shortcut", action="store_true", dest="startmenu_shortcut", + help="Use this, if there is no link in start menu to launch Rare") subparsers = parser.add_subparsers(title="Commands", dest="subparser") launch_parser = subparsers.add_parser("launch") @@ -23,6 +30,13 @@ def main(): args = parser.parse_args() + if args.desktop_shortcut: + utils.create_rare_desktop_link("desktop") + print("Link created") + if args.startmenu_shortcut: + utils.create_rare_desktop_link("start_menu") + print("link created") + if args.version: print(__version__) exit(0) diff --git a/rare/utils/utils.py b/rare/utils/utils.py index ec18e899..f295a28b 100644 --- a/rare/utils/utils.py +++ b/rare/utils/utils.py @@ -218,8 +218,58 @@ def get_size(b: int) -> str: b /= 1024 +def create_rare_desktop_link(type_of_link): + # Linux + if os.name == "posix": + if type_of_link == "desktop": + path = os.path.expanduser(f"~/Desktop/") + elif type_of_link == "start_menu": + path = os.path.expanduser("~/.local/share/applications/") + else: + return + + with open(f"{path}Rare.desktop", "w") as desktop_file: + desktop_file.write("[Desktop Entry]\n" + f"Name=Rare\n" + f"Type=Application\n" + f"Icon={os.path.join(style_path, 'Logo.png')}\n" + f"Exec={os.path.abspath(sys.argv[0])}\n" + "Terminal=false\n" + "StartupWMClass=rare\n" + ) + desktop_file.close() + os.chmod(os.path.expanduser(f"{path}Rare.desktop"), 0o755) + + elif os.name == "nt": + # Target of shortcut + if type_of_link == "desktop": + target_folder = os.path.expanduser('~/Desktop/') + elif type_of_link == "start_menu": + target_folder = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") + else: + logger.warning("No valid type of link") + return + linkName = "Rare.lnk" + + # Path to location of link file + pathLink = os.path.join(target_folder, linkName) + + # Add shortcut + shell = Dispatch('WScript.Shell') + shortcut = shell.CreateShortCut(pathLink) + shortcut.Targetpath = os.path.abspath(sys.argv[0]) + shortcut.Arguments = "" + shortcut.WorkingDirectory = os.getcwd() + + # Icon + shortcut.IconLocation = os.path.join(style_path, "Logo.ico") + + shortcut.save() + + 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), igame.app_name, 'Thumbnail.png')): From 432cdede3a8debca7ee934bb2e13409db2eb091b Mon Sep 17 00:00:00 2001 From: Dummerle Date: Sat, 22 May 2021 20:28:50 +0200 Subject: [PATCH 04/22] Add logir buttons with functions; optimize repair --- rare/components/tab_widget.py | 5 +- rare/components/tabs/downloads/__init__.py | 2 +- rare/components/tabs/settings/rare.py | 53 ++- rare/ui/components/tabs/settings/rare.py | 134 +++--- rare/ui/components/tabs/settings/rare.ui | 472 +++++++++++---------- 5 files changed, 388 insertions(+), 278 deletions(-) diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index c575d051..a980423f 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -83,7 +83,7 @@ class TabWidget(QTabWidget): # install game self.games_tab.uninstalled_info_widget.info.install_game.connect(self.install_game) # repair game - self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.downloadTab.install_game( + self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.start_download( InstallOptions(app_name, core.get_installed_game(app_name).install_path, repair=True))) # Finished sync @@ -102,12 +102,13 @@ class TabWidget(QTabWidget): path, max_workers, force, ignore_free_space, dl_only = infos options = InstallOptions(app_name=app_name, max_workers=max_workers, path=path, force=force, ignore_free_space=ignore_free_space, download_only=dl_only) - self.setCurrentIndex(1) + self.start_download(options) def start_download(self, options): downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1 self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else "")) + self.setCurrentIndex(1) self.downloadTab.install_game(options) def game_imported(self, app_name: str): diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 5ca1a62e..f3d79d52 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -136,7 +136,7 @@ class DownloadTab(QWidget): return # Information - if not from_update: + if not from_update and not options.repair: if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): self.finished.emit(False, None) return diff --git a/rare/components/tabs/settings/rare.py b/rare/components/tabs/settings/rare.py index 3aaff5e4..2fafc3d9 100644 --- a/rare/components/tabs/settings/rare.py +++ b/rare/components/tabs/settings/rare.py @@ -9,6 +9,7 @@ from PyQt5.QtWidgets import QFileDialog, QWidget 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 @@ -99,10 +100,58 @@ class RareSettings(QWidget, Ui_RareSettings): lambda: self.settings.setValue("save_size", self.save_size.isChecked()) ) + if os.name == "posix": + self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop") + self.start_menu_link = os.path.expanduser("~/.local/share/applications/Rare.desktop") + elif os.name == "nt": + self.desktop_file = os.path.expanduser("~/Desktop/Rare.lnk") + self.start_menu_link = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") + else: + self.desktop_file = "" + self.start_menu_link = "" + + if os.path.exists(self.desktop_file): + self.desktop_link.setText(self.tr("Remove desktop link")) + if os.path.exists(self.start_menu_link): + self.startmenu_link.setText(self.tr("Remove start menu link")) + + self.desktop_link.clicked.connect(self.create_desktop_link) + self.startmenu_link.clicked.connect(self.create_start_menu_link) + 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") + # get size of logdir + size = 0 + for i in os.listdir(logdir): + size += os.path.getsize(os.path.join(logdir, i)) + + self.log_dir_size_label.setText(utils.get_size(size)) # TODO: Implement - self.log_dir_clean_button.setVisible(False) - self.log_dir_size_label.setVisible(False) + # self.log_dir_clean_button.setVisible(False) + # 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) + self.log_dir_size_label.setText("0KB") + + def create_start_menu_link(self): + if not os.path.exists(self.start_menu_link): + utils.create_rare_desktop_link("start_menu") + self.startmenu_link.setText(self.tr("Remove start menu link")) + else: + os.remove(self.start_menu_link) + self.startmenu_link.setText(self.tr("Create start menu link")) + + def create_desktop_link(self): + if not os.path.exists(self.desktop_file): + utils.create_rare_desktop_link("start_menu") + self.desktop_link.setText(self.tr("Remove Desktop link")) + else: + os.remove(self.desktop_file) + self.desktop_link.setText(self.tr("Create desktop link")) def on_color_select_changed(self, color): if color: diff --git a/rare/ui/components/tabs/settings/rare.py b/rare/ui/components/tabs/settings/rare.py index b7f6b1ff..abbd889e 100644 --- a/rare/ui/components/tabs/settings/rare.py +++ b/rare/ui/components/tabs/settings/rare.py @@ -14,52 +14,25 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_RareSettings(object): def setupUi(self, RareSettings): RareSettings.setObjectName("RareSettings") + RareSettings.resize(526, 486) self.rare_layout = QtWidgets.QGridLayout(RareSettings) self.rare_layout.setObjectName("rare_layout") - self.interface_group = QtWidgets.QGroupBox(RareSettings) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.interface_group.sizePolicy().hasHeightForWidth()) - self.interface_group.setSizePolicy(sizePolicy) - self.interface_group.setObjectName("interface_group") - self.interface_layout = QtWidgets.QGridLayout(self.interface_group) - self.interface_layout.setObjectName("interface_layout") - self.color_select = QtWidgets.QComboBox(self.interface_group) - self.color_select.setObjectName("color_select") - self.color_select.addItem("") - self.interface_layout.addWidget(self.color_select, 1, 1, 1, 1) - self.style_label = QtWidgets.QLabel(self.interface_group) - self.style_label.setObjectName("style_label") - self.interface_layout.addWidget(self.style_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) - self.lang_label = QtWidgets.QLabel(self.interface_group) - self.lang_label.setObjectName("lang_label") - self.interface_layout.addWidget(self.lang_label, 0, 0, 1, 1, QtCore.Qt.AlignRight) - self.lang_select = QtWidgets.QComboBox(self.interface_group) - self.lang_select.setObjectName("lang_select") - self.interface_layout.addWidget(self.lang_select, 0, 1, 1, 1) - self.color_label = QtWidgets.QLabel(self.interface_group) - self.color_label.setObjectName("color_label") - self.interface_layout.addWidget(self.color_label, 1, 0, 1, 1, QtCore.Qt.AlignRight) - self.interface_info = QtWidgets.QLabel(self.interface_group) - font = QtGui.QFont() - font.setItalic(True) - self.interface_info.setFont(font) - self.interface_info.setWordWrap(True) - self.interface_info.setObjectName("interface_info") - self.interface_layout.addWidget(self.interface_info, 3, 0, 1, 3) - self.style_select = QtWidgets.QComboBox(self.interface_group) - self.style_select.setObjectName("style_select") - self.style_select.addItem("") - self.interface_layout.addWidget(self.style_select, 2, 1, 1, 1) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.interface_layout.addItem(spacerItem, 1, 2, 1, 1) - self.rare_layout.addWidget(self.interface_group, 1, 0, 1, 1, QtCore.Qt.AlignTop) - self.img_dir_group = QtWidgets.QGroupBox(RareSettings) - self.img_dir_group.setObjectName("img_dir_group") - 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, QtCore.Qt.AlignTop) + self.rpc_layout = QtWidgets.QVBoxLayout() + self.rpc_layout.setObjectName("rpc_layout") + self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1) + self.groupBox = QtWidgets.QGroupBox(RareSettings) + self.groupBox.setObjectName("groupBox") + self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox) + self.verticalLayout.setObjectName("verticalLayout") + self.desktop_link = QtWidgets.QPushButton(self.groupBox) + self.desktop_link.setObjectName("desktop_link") + self.verticalLayout.addWidget(self.desktop_link) + self.startmenu_link = QtWidgets.QPushButton(self.groupBox) + self.startmenu_link.setObjectName("startmenu_link") + self.verticalLayout.addWidget(self.startmenu_link) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1) self.settings_group = QtWidgets.QGroupBox(RareSettings) self.settings_group.setObjectName("settings_group") self.behavior_layout = QtWidgets.QGridLayout(self.settings_group) @@ -84,7 +57,7 @@ class Ui_RareSettings(object): 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.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop) + self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1) self.log_dir_group = QtWidgets.QGroupBox(RareSettings) self.log_dir_group.setObjectName("log_dir_group") self.log_dir_layout = QtWidgets.QVBoxLayout(self.log_dir_group) @@ -101,11 +74,52 @@ class Ui_RareSettings(object): self.log_dir_size_label.setObjectName("log_dir_size_label") self.log_dir_layout.addWidget(self.log_dir_size_label) self.rare_layout.addWidget(self.log_dir_group, 0, 1, 1, 1, QtCore.Qt.AlignTop) - self.rpc_layout = QtWidgets.QVBoxLayout() - self.rpc_layout.setObjectName("rpc_layout") - self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1) - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.rare_layout.addItem(spacerItem2, 2, 1, 1, 1) + self.interface_group = QtWidgets.QGroupBox(RareSettings) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.interface_group.sizePolicy().hasHeightForWidth()) + self.interface_group.setSizePolicy(sizePolicy) + self.interface_group.setObjectName("interface_group") + self.interface_layout = QtWidgets.QGridLayout(self.interface_group) + self.interface_layout.setObjectName("interface_layout") + self.color_select = QtWidgets.QComboBox(self.interface_group) + self.color_select.setObjectName("color_select") + self.color_select.addItem("") + self.interface_layout.addWidget(self.color_select, 1, 1, 1, 1) + self.style_label = QtWidgets.QLabel(self.interface_group) + self.style_label.setObjectName("style_label") + self.interface_layout.addWidget(self.style_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) + self.style_select = QtWidgets.QComboBox(self.interface_group) + self.style_select.setObjectName("style_select") + self.style_select.addItem("") + self.interface_layout.addWidget(self.style_select, 2, 1, 1, 1) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.interface_layout.addItem(spacerItem2, 1, 2, 1, 1) + self.interface_info = QtWidgets.QLabel(self.interface_group) + font = QtGui.QFont() + font.setItalic(True) + self.interface_info.setFont(font) + self.interface_info.setWordWrap(True) + self.interface_info.setObjectName("interface_info") + self.interface_layout.addWidget(self.interface_info, 3, 0, 1, 3) + self.lang_label = QtWidgets.QLabel(self.interface_group) + self.lang_label.setObjectName("lang_label") + self.interface_layout.addWidget(self.lang_label, 0, 0, 1, 1, QtCore.Qt.AlignRight) + self.lang_select = QtWidgets.QComboBox(self.interface_group) + self.lang_select.setObjectName("lang_select") + self.interface_layout.addWidget(self.lang_select, 0, 1, 1, 1) + self.color_label = QtWidgets.QLabel(self.interface_group) + self.color_label.setObjectName("color_label") + self.interface_layout.addWidget(self.color_label, 1, 0, 1, 1, QtCore.Qt.AlignRight) + self.rare_layout.addWidget(self.interface_group, 1, 0, 1, 1, QtCore.Qt.AlignTop) + self.img_dir_group = QtWidgets.QGroupBox(RareSettings) + self.img_dir_group.setObjectName("img_dir_group") + 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, QtCore.Qt.AlignTop) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.rare_layout.addItem(spacerItem3, 3, 0, 1, 1) self.retranslateUi(RareSettings) QtCore.QMetaObject.connectSlotsByName(RareSettings) @@ -113,14 +127,9 @@ class Ui_RareSettings(object): def retranslateUi(self, RareSettings): _translate = QtCore.QCoreApplication.translate RareSettings.setWindowTitle(_translate("RareSettings", "RareSettings")) - self.interface_group.setTitle(_translate("RareSettings", "Interface")) - self.color_select.setItemText(0, _translate("RareSettings", "None")) - self.style_label.setText(_translate("RareSettings", "Style Sheet")) - self.lang_label.setText(_translate("RareSettings", "Language")) - self.color_label.setText(_translate("RareSettings", "Color Scheme")) - self.interface_info.setText(_translate("RareSettings", "Restart Rare to apply.")) - self.style_select.setItemText(0, _translate("RareSettings", "None")) - self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory")) + self.groupBox.setTitle(_translate("RareSettings", "Shortcuts")) + self.desktop_link.setText(_translate("RareSettings", "Create Desktop link")) + self.startmenu_link.setText(_translate("RareSettings", "Create start menu link")) self.settings_group.setTitle(_translate("RareSettings", "Behavior")) self.save_size.setText(_translate("RareSettings", "Restore window size on application startup")) self.notification.setText(_translate("RareSettings", "Show notification on download completion")) @@ -131,11 +140,18 @@ class Ui_RareSettings(object): 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.color_select.setItemText(0, _translate("RareSettings", "None")) + self.style_label.setText(_translate("RareSettings", "Style Sheet")) + self.style_select.setItemText(0, _translate("RareSettings", "None")) + self.interface_info.setText(_translate("RareSettings", "Restart Rare to apply.")) + self.lang_label.setText(_translate("RareSettings", "Language")) + self.color_label.setText(_translate("RareSettings", "Color Scheme")) + self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory")) if __name__ == "__main__": import sys - app = QtWidgets.QApplication(sys.argv) RareSettings = QtWidgets.QWidget() ui = Ui_RareSettings() diff --git a/rare/ui/components/tabs/settings/rare.ui b/rare/ui/components/tabs/settings/rare.ui index 0b0c6193..78c43ff8 100644 --- a/rare/ui/components/tabs/settings/rare.ui +++ b/rare/ui/components/tabs/settings/rare.ui @@ -1,219 +1,263 @@ - RareSettings - - - RareSettings + RareSettings + + + + 0 + 0 + 526 + 486 + + + + RareSettings + + + + + + + + + Shortcuts + + + + + + Create Desktop link - - - - - - 0 - 0 - - - - Interface - - - - - - - None - - - - - - - - Style Sheet - - - - - - - Language - - - - - - - - - - Color Scheme - - - - - - - - true - - - - Restart Rare to apply. - - - true - - - - - - - - None - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Image Cache Directory - - - - - - - - Behavior - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Restore window size on application startup - - - - - - - Show notification on download completion - - - - - - - Automatically sync with cloud - - - - - - - Confirm game launch - - - - - - - Update games on application startup - - - - - - - Exit to System tray - - - - - - - - - - Logs - - - - - - Open Log directory - - - - - - - Clean Log directory - - - - - - - - - - true - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + + + + + Create start menu link + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + - - + + + + + Behavior + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Restore window size on application startup + + + + + + + Show notification on download completion + + + + + + + Automatically sync with cloud + + + + + + + Confirm game launch + + + + + + + Update games on application startup + + + + + + + Exit to System tray + + + + + + + + + + Logs + + + + + + Open Log directory + + + + + + + Clean Log directory + + + + + + + + + + true + + + + + + + + + + + 0 + 0 + + + + Interface + + + + + + + None + + + + + + + + Style Sheet + + + + + + + + None + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + true + + + + Restart Rare to apply. + + + true + + + + + + + Language + + + + + + + + + + Color Scheme + + + + + + + + + + Image Cache Directory + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + From 56f79fb5944986e555c5ad773796ac881e50106a Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sat, 22 May 2021 23:18:58 +0300 Subject: [PATCH 05/22] Make shortcuts align top properly --- rare/ui/components/tabs/settings/rare.py | 19 +++++++--------- rare/ui/components/tabs/settings/rare.ui | 29 ++++-------------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/rare/ui/components/tabs/settings/rare.py b/rare/ui/components/tabs/settings/rare.py index abbd889e..6e948018 100644 --- a/rare/ui/components/tabs/settings/rare.py +++ b/rare/ui/components/tabs/settings/rare.py @@ -14,7 +14,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_RareSettings(object): def setupUi(self, RareSettings): RareSettings.setObjectName("RareSettings") - RareSettings.resize(526, 486) self.rare_layout = QtWidgets.QGridLayout(RareSettings) self.rare_layout.setObjectName("rare_layout") self.rpc_layout = QtWidgets.QVBoxLayout() @@ -30,15 +29,13 @@ class Ui_RareSettings(object): self.startmenu_link = QtWidgets.QPushButton(self.groupBox) self.startmenu_link.setObjectName("startmenu_link") self.verticalLayout.addWidget(self.startmenu_link) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem) - self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1) + self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1, QtCore.Qt.AlignTop) self.settings_group = QtWidgets.QGroupBox(RareSettings) self.settings_group.setObjectName("settings_group") self.behavior_layout = QtWidgets.QGridLayout(self.settings_group) self.behavior_layout.setObjectName("behavior_layout") - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.behavior_layout.addItem(spacerItem1, 2, 1, 2, 1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.behavior_layout.addItem(spacerItem, 2, 1, 2, 1) self.save_size = QtWidgets.QCheckBox(self.settings_group) self.save_size.setObjectName("save_size") self.behavior_layout.addWidget(self.save_size, 5, 0, 1, 1) @@ -57,7 +54,7 @@ class Ui_RareSettings(object): 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.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1) + self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop) self.log_dir_group = QtWidgets.QGroupBox(RareSettings) self.log_dir_group.setObjectName("log_dir_group") self.log_dir_layout = QtWidgets.QVBoxLayout(self.log_dir_group) @@ -94,8 +91,8 @@ class Ui_RareSettings(object): self.style_select.setObjectName("style_select") self.style_select.addItem("") self.interface_layout.addWidget(self.style_select, 2, 1, 1, 1) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.interface_layout.addItem(spacerItem2, 1, 2, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.interface_layout.addItem(spacerItem1, 1, 2, 1, 1) self.interface_info = QtWidgets.QLabel(self.interface_group) font = QtGui.QFont() font.setItalic(True) @@ -118,8 +115,8 @@ 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, QtCore.Qt.AlignTop) - spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.rare_layout.addItem(spacerItem3, 3, 0, 1, 1) + 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) diff --git a/rare/ui/components/tabs/settings/rare.ui b/rare/ui/components/tabs/settings/rare.ui index 78c43ff8..377f874c 100644 --- a/rare/ui/components/tabs/settings/rare.ui +++ b/rare/ui/components/tabs/settings/rare.ui @@ -2,14 +2,6 @@ RareSettings - - - 0 - 0 - 526 - 486 - - RareSettings @@ -17,7 +9,7 @@ - + Shortcuts @@ -37,23 +29,10 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - + Behavior @@ -243,8 +222,8 @@ - - + + Qt::Vertical From 4ff308a4589348ca5901bf4c9ed0eceee84f45de Mon Sep 17 00:00:00 2001 From: ChemicalXandco <32775248+ChemicalXandco@users.noreply.github.com> Date: Mon, 24 May 2021 17:29:50 +0100 Subject: [PATCH 06/22] update legendary https://github.com/derrod/legendary/commit/7ae4eda5b8b74239f05cfd9395124fc054e0c5c5 --- custom_legendary/utils/manifests.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/custom_legendary/utils/manifests.py b/custom_legendary/utils/manifests.py index 44b682c0..744ffa28 100644 --- a/custom_legendary/utils/manifests.py +++ b/custom_legendary/utils/manifests.py @@ -20,8 +20,19 @@ def combine_manifests(base_manifest: Manifest, delta_manifest: Manifest): base_manifest.file_manifest_list.count = len(base_manifest.file_manifest_list.elements) base_manifest.file_manifest_list._path_map = None - # add chunks from delta manifest to main manifest and again clear path caches - base_manifest.chunk_data_list.elements.extend(delta_manifest.chunk_data_list.elements) + # ensure guid map exists + try: + base_manifest.chunk_data_list.get_chunk_by_guid(0) + except: + pass + + # add new chunks from delta manifest to main manifest and again clear maps and update count + existing_chunk_guids = base_manifest.chunk_data_list._guid_int_map.keys() + + for chunk in delta_manifest.chunk_data_list.elements: + if chunk.guid_num not in existing_chunk_guids: + base_manifest.chunk_data_list.elements.append(chunk) + base_manifest.chunk_data_list.count = len(base_manifest.chunk_data_list.elements) base_manifest.chunk_data_list._guid_map = None base_manifest.chunk_data_list._guid_int_map = None From 3d31807d15e93c209b9d8d1814a5f2747b265f80 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 25 May 2021 17:44:21 +0200 Subject: [PATCH 07/22] Add styles to MANIFEST.in --- MANIFEST.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3ee6d4e8..f1893424 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include README.md include rare/languages/*.qm -include rare/styles/* \ No newline at end of file +include rare/styles/* +include rare/styles/qss/* +include rare/styles/colors/* From 5ccf73d7376e6366dfe91c585dc074de10ece46b Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Fri, 21 May 2021 02:47:03 +0300 Subject: [PATCH 08/22] Update install dialog and remove the extraneous install info dialog --- rare/components/dialogs/install_dialog.py | 179 +++++++++++-------- rare/components/tab_widget.py | 2 +- rare/components/tabs/downloads/__init__.py | 16 +- rare/ui/components/dialogs/install_dialog.py | 126 +++++++++++++ rare/ui/components/dialogs/install_dialog.ui | 179 +++++++++++++++++++ rare/utils/extra_widgets.py | 3 + 6 files changed, 413 insertions(+), 92 deletions(-) create mode 100644 rare/ui/components/dialogs/install_dialog.py create mode 100644 rare/ui/components/dialogs/install_dialog.ui diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 92127ac0..9da47805 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -1,24 +1,32 @@ import os -from PyQt5.QtWidgets import QDialog, QFormLayout, QVBoxLayout, QSpinBox, QFileDialog, QLabel, QPushButton, QHBoxLayout, \ - QCheckBox +from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot +from PyQt5.QtWidgets import QDialog, QFileDialog from custom_legendary.core import LegendaryCore +from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.utils.extra_widgets import PathEdit from rare.utils.utils import get_size -class InstallDialog(QDialog): - infos = 0 +class InstallDialog(QDialog, Ui_InstallDialog): + infos = False def __init__(self, app_name, core: LegendaryCore, update=False): super(InstallDialog, self).__init__() - self.layout = QVBoxLayout() + self.setupUi(self) + self.core = core + self.app_name = app_name self.game = self.core.get_game(app_name) - self.form = QFormLayout() self.update_game = update - self.layout.addWidget(QLabel(self.tr("

Install {}

").format(self.game.app_title))) + + self.threadpool = QThreadPool.globalInstance() + + header_text = self.tr("Update") if update else self.tr("Install") + self.install_dialog_label.setText("

{} \"{}\"

".format(header_text, self.game.app_title)) + # TODO: Get application name from QApplication instance + self.setWindowTitle("{} - {} \"{}\"".format("Rare", header_text, self.game.app_title)) if self.core.lgd.config.has_option("Legendary", "install_dir"): default_path = self.core.lgd.config.get("Legendary", "install_dir") @@ -26,92 +34,107 @@ class InstallDialog(QDialog): default_path = os.path.expanduser("~/legendary") if not default_path: default_path = os.path.expanduser("~/legendary") - if not update: - self.install_path_field = PathEdit(text=default_path, file_type=QFileDialog.DirectoryOnly) - self.form.addRow(QLabel("Install directory"), self.install_path_field) + + self.install_dir_path = PathEdit(text=default_path, + file_type=QFileDialog.DirectoryOnly, + edit_func=self.on_install_dir_text_changed) + self.install_dir_layout.addWidget(self.install_dir_path) + + if update: + self.install_dir_label.setVisible(False) + self.install_dir_path.setVisible(False) if self.core.lgd.config.has_option("Legendary", "max_workers"): max_workers = self.core.lgd.config.get("Legendary", "max_workers") else: max_workers = 0 + self.max_workers_spin.setValue(int(max_workers)) - self.max_workes = QSpinBox() - self.max_workes.setValue(int(max_workers)) - self.form.addRow(QLabel(self.tr("Max workers (0: Default)")), self.max_workes) + self.get_install_info(app_name, default_path) - self.force = QCheckBox() - self.force.setChecked(False) - self.form.addRow(QLabel(self.tr("Force download")), self.force) + self.install_button.clicked.connect(self.on_install_button_clicked) + self.cancel_button.clicked.connect(self.on_cancel_button_clicked) - self.ignore_free_space = QCheckBox() - self.ignore_free_space.setChecked(False) - self.form.addRow(QLabel(self.tr("Ignore free space (Warning!)")), self.ignore_free_space) - - self.download_only = QCheckBox() - self.download_only.setChecked(False) - self.form.addRow(QLabel(self.tr("Do not install game")), self.download_only) - - self.layout.addLayout(self.form) - - self.ok_btn = QPushButton("Next") - self.ok_btn.clicked.connect(self.ok) - self.cancel = QPushButton("Cancel") - self.cancel.clicked.connect(lambda: self.close()) - - self.button_layout = QHBoxLayout() - self.button_layout.addStretch(1) - self.button_layout.addWidget(self.ok_btn) - self.button_layout.addWidget(self.cancel) - - self.layout.addLayout(self.button_layout) - - self.setLayout(self.layout) + self.resize(self.minimumSizeHint()) + self.setFixedSize(self.size()) def get_information(self, path=None): if path: - self.install_path_field.text_edit.setText(path) + self.install_dir_path.setText(path) self.exec_() return self.infos - def ok(self): - self.infos = self.install_path_field.text() if not self.update_game else None, \ - self.max_workes.value(), \ - self.force.isChecked(), \ - self.ignore_free_space.isChecked(), \ - self.download_only.isChecked() + def get_install_info(self, app_name, default_path): + updating_text = self.tr("Updating...") + self.download_size_info_label.setText(updating_text) + self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") + self.install_size_info_label.setText(updating_text) + self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") + self.install_button.setEnabled(False) + info_worker = InstallInfoWorker(app_name, default_path, self.core) + info_worker.setAutoDelete(True) + info_worker.signals.finished.connect(self.on_worker_finished) + self.threadpool.start(info_worker) + + def on_install_dir_text_changed(self, path: str): + self.get_install_info(self.app_name, path) + + def on_install_button_clicked(self): + self.infos = self.install_dir_path.text() if not self.update_game else None, \ + self.max_workers_spin.value(), \ + self.force_download_check.isChecked(), \ + self.ignore_space_check.isChecked(), \ + self.download_only_check.isChecked() + self.threadpool.clear() self.close() - -class InstallInfoDialog(QDialog): - accept: bool = False - - def __init__(self, dl_size, install_size): - super(InstallInfoDialog, self).__init__() - self.layout = QVBoxLayout() - self.infos = QLabel(self.tr( - "Download size: {}\nInstall size: {}").format(get_size(dl_size), get_size(install_size))) - self.layout.addWidget(self.infos) - - self.btn_layout = QHBoxLayout() - self.install_btn = QPushButton(self.tr("Install")) - self.install_btn.clicked.connect(self.install) - self.cancel_button = QPushButton(self.tr("Cancel")) - self.cancel_button.clicked.connect(self.cancel) - self.btn_layout.addStretch(1) - self.btn_layout.addWidget(self.install_btn) - self.btn_layout.addWidget(self.cancel_button) - self.layout.addLayout(self.btn_layout) - - self.setLayout(self.layout) - - def get_accept(self): - self.exec_() - return self.accept - - def install(self): - self.accept = True + def on_cancel_button_clicked(self): + self.threadpool.clear() self.close() - def cancel(self): - self.accept = False - self.close() + def on_worker_finished(self, info: tuple): + download_size, install_size = info + + # TODO: Check available size and act accordingly + # TODO: (show message in label | color it | disable install unless ignore) + # TODO: Find a way to get the installation size delta and show it + + if download_size is not None and install_size is not None: + if download_size: + self.download_size_info_label.setText("{}".format(get_size(download_size))) + self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") + self.install_button.setEnabled(True) + else: + self.install_size_info_label.setText(self.tr("Game already installed")) + self.install_size_info_label.setStyleSheet("font-style: italics; font-weight: normal") + self.install_size_info_label.setText("{}".format(get_size(install_size))) + self.install_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") + else: + self.download_size_info_label.setText("Error") + self.install_size_info_label.setText("Error") + + +class InstallInfoWorkerSignals(QObject): + finished = pyqtSignal(tuple) + + +class InstallInfoWorker(QRunnable): + + def __init__(self, app_name, path, core: LegendaryCore): + super(InstallInfoWorker, self).__init__() + self.signals = InstallInfoWorkerSignals() + self.core = core + self.app_name = app_name + self.path = path + + @pyqtSlot() + def run(self): + try: + dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( + app_name=self.app_name, + base_path=self.path, + ) + self.signals.finished.emit((analysis.dl_size, analysis.install_size)) + except: + self.signals.finished.emit((None, None)) + return diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index a980423f..5818536e 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -98,7 +98,7 @@ class TabWidget(QTabWidget): def install_game(self, app_name, disable_path=False): infos = InstallDialog(app_name, self.core, disable_path).get_information() - if infos != 0: + if infos: path, max_workers, force, ignore_free_space, dl_only = infos options = InstallOptions(app_name=app_name, max_workers=max_workers, path=path, force=force, ignore_free_space=ignore_free_space, download_only=dl_only) diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index f3d79d52..78cf3fbc 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -10,7 +10,7 @@ from custom_legendary.core import LegendaryCore from custom_legendary.models.downloading import UIUpdate from custom_legendary.models.game import Game, InstalledGame from custom_legendary.utils.selective_dl import games -from rare.components.dialogs.install_dialog import InstallInfoDialog, InstallDialog +from rare.components.dialogs.install_dialog import InstallDialog from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget from rare.components.tabs.downloads.download_thread import DownloadThread from rare.utils.models import InstallOptions @@ -131,16 +131,6 @@ class DownloadTab(QWidget): str(e)) return - if not analysis.dl_size: - QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists")) - return - - # Information - if not from_update and not options.repair: - if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): - self.finished.emit(False, None) - return - if self.active_game is None: self.start_installation(dlm, game, status_queue, igame, repair_file, options, analysis, options.download_only) @@ -284,10 +274,10 @@ class DownloadTab(QWidget): else: self.install_game(InstallOptions(app_name=app_name), True) return - if infos != 0: + if infos: path, max_workers, force, ignore_free_space, dl_only = infos self.install_game(InstallOptions(app_name=app_name, max_workers=max_workers, path=path, - force=force, ignore_free_space=ignore_free_space, dl_only=dl_only), True) + force=force, ignore_free_space=ignore_free_space, download_only=dl_only), True) else: self.update_widgets[app_name].update_button.setDisabled(False) self.update_widgets[app_name].update_with_settings.setDisabled(False) diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py new file mode 100644 index 00000000..e693ce8c --- /dev/null +++ b/rare/ui/components/dialogs/install_dialog.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'install_dialog.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_InstallDialog(object): + def setupUi(self, InstallDialog): + InstallDialog.setObjectName("InstallDialog") + self.install_dialog_layout = QtWidgets.QGridLayout(InstallDialog) + self.install_dialog_layout.setObjectName("install_dialog_layout") + self.ignore_space_label = QtWidgets.QLabel(InstallDialog) + self.ignore_space_label.setObjectName("ignore_space_label") + self.install_dialog_layout.addWidget(self.ignore_space_label, 4, 0, 1, 1, QtCore.Qt.AlignRight) + self.install_dialog_label = QtWidgets.QLabel(InstallDialog) + self.install_dialog_label.setObjectName("install_dialog_label") + self.install_dialog_layout.addWidget(self.install_dialog_label, 0, 0, 1, 3) + self.download_size_label = QtWidgets.QLabel(InstallDialog) + self.download_size_label.setObjectName("download_size_label") + self.install_dialog_layout.addWidget(self.download_size_label, 6, 0, 1, 1, QtCore.Qt.AlignRight) + self.install_size_info_label = QtWidgets.QLabel(InstallDialog) + self.install_size_info_label.setText("") + self.install_size_info_label.setObjectName("install_size_info_label") + self.install_dialog_layout.addWidget(self.install_size_info_label, 7, 1, 1, 2) + self.install_dir_label = QtWidgets.QLabel(InstallDialog) + self.install_dir_label.setObjectName("install_dir_label") + self.install_dialog_layout.addWidget(self.install_dir_label, 1, 0, 1, 1, QtCore.Qt.AlignRight) + spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.install_dialog_layout.addItem(spacerItem, 3, 2, 1, 1) + self.ignore_space_info_label = QtWidgets.QLabel(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.ignore_space_info_label.setFont(font) + self.ignore_space_info_label.setObjectName("ignore_space_info_label") + self.install_dialog_layout.addWidget(self.ignore_space_info_label, 4, 2, 1, 1) + self.force_download_check = QtWidgets.QCheckBox(InstallDialog) + self.force_download_check.setObjectName("force_download_check") + self.install_dialog_layout.addWidget(self.force_download_check, 3, 1, 1, 1) + self.force_download_label = QtWidgets.QLabel(InstallDialog) + self.force_download_label.setObjectName("force_download_label") + self.install_dialog_layout.addWidget(self.force_download_label, 3, 0, 1, 1, QtCore.Qt.AlignRight) + self.button_layout = QtWidgets.QHBoxLayout() + self.button_layout.setObjectName("button_layout") + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.button_layout.addItem(spacerItem1) + self.cancel_button = QtWidgets.QPushButton(InstallDialog) + self.cancel_button.setObjectName("cancel_button") + self.button_layout.addWidget(self.cancel_button) + self.install_button = QtWidgets.QPushButton(InstallDialog) + self.install_button.setObjectName("install_button") + self.button_layout.addWidget(self.install_button) + self.install_dialog_layout.addLayout(self.button_layout, 8, 0, 1, 3) + self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog) + self.max_workers_spin.setObjectName("max_workers_spin") + self.install_dialog_layout.addWidget(self.max_workers_spin, 2, 1, 1, 1) + self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog) + self.ignore_space_check.setObjectName("ignore_space_check") + self.install_dialog_layout.addWidget(self.ignore_space_check, 4, 1, 1, 1) + self.install_size_label = QtWidgets.QLabel(InstallDialog) + self.install_size_label.setObjectName("install_size_label") + self.install_dialog_layout.addWidget(self.install_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight) + self.max_workers_info_label = QtWidgets.QLabel(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.max_workers_info_label.setFont(font) + self.max_workers_info_label.setObjectName("max_workers_info_label") + self.install_dialog_layout.addWidget(self.max_workers_info_label, 2, 2, 1, 1) + self.install_dir_layout = QtWidgets.QHBoxLayout() + self.install_dir_layout.setObjectName("install_dir_layout") + self.install_dialog_layout.addLayout(self.install_dir_layout, 1, 1, 1, 2) + self.download_size_info_label = QtWidgets.QLabel(InstallDialog) + self.download_size_info_label.setText("") + self.download_size_info_label.setObjectName("download_size_info_label") + self.install_dialog_layout.addWidget(self.download_size_info_label, 6, 1, 1, 2) + self.max_workers_label = QtWidgets.QLabel(InstallDialog) + self.max_workers_label.setObjectName("max_workers_label") + self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) + self.download_only_label = QtWidgets.QLabel(InstallDialog) + self.download_only_label.setObjectName("download_only_label") + self.install_dialog_layout.addWidget(self.download_only_label, 5, 0, 1, 1, QtCore.Qt.AlignRight) + self.download_only_check = QtWidgets.QCheckBox(InstallDialog) + self.download_only_check.setText("") + self.download_only_check.setObjectName("download_only_check") + self.install_dialog_layout.addWidget(self.download_only_check, 5, 1, 1, 1) + self.download_only_info_label = QtWidgets.QLabel(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.download_only_info_label.setFont(font) + self.download_only_info_label.setObjectName("download_only_info_label") + self.install_dialog_layout.addWidget(self.download_only_info_label, 5, 2, 1, 1) + + self.retranslateUi(InstallDialog) + QtCore.QMetaObject.connectSlotsByName(InstallDialog) + + def retranslateUi(self, InstallDialog): + _translate = QtCore.QCoreApplication.translate + self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) + self.install_dialog_label.setText(_translate("InstallDialog", "error")) + self.download_size_label.setText(_translate("InstallDialog", "Download size")) + self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) + self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!")) + self.force_download_label.setText(_translate("InstallDialog", "Force download")) + self.cancel_button.setText(_translate("InstallDialog", "Cancel")) + self.install_button.setText(_translate("InstallDialog", "Install")) + self.install_size_label.setText(_translate("InstallDialog", "Total install size")) + self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)")) + self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) + self.download_only_label.setText(_translate("InstallDialog", "Download only")) + self.download_only_info_label.setText(_translate("InstallDialog", "Do not try to install.")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + InstallDialog = QtWidgets.QDialog() + ui = Ui_InstallDialog() + ui.setupUi(InstallDialog) + InstallDialog.show() + sys.exit(app.exec_()) diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui new file mode 100644 index 00000000..576482dd --- /dev/null +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -0,0 +1,179 @@ + + + InstallDialog + + + + + + Ignore free space + + + + + + + error + + + + + + + Download size + + + + + + + + + + + + + + Install directory + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + true + + + + Use with caution! + + + + + + + + + + Force download + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Install + + + + + + + + + + + + + + + Total install size + + + + + + + + true + + + + Less is slower. (0: Default) + + + + + + + + + + + + + + + + + Max workers + + + + + + + Download only + + + + + + + + + + + + + + + true + + + + Do not try to install. + + + + + + + + diff --git a/rare/utils/extra_widgets.py b/rare/utils/extra_widgets.py index 31db79cf..121ab59d 100644 --- a/rare/utils/extra_widgets.py +++ b/rare/utils/extra_widgets.py @@ -152,6 +152,9 @@ class PathEdit(QWidget, Ui_PathEdit): def text(self): return self.text_edit.text() + def setText(self, text: str): + self.text_edit.setText(text) + def save(self): self.save_func() self.save_path_button.setDisabled(True) From 1131edda207712eb84104eae05b29655ec4749d8 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Fri, 21 May 2021 17:48:15 +0300 Subject: [PATCH 09/22] Add selective downloads --- rare/components/dialogs/install_dialog.py | 105 ++++++--- rare/components/tab_widget.py | 9 +- rare/components/tabs/downloads/__init__.py | 52 +---- rare/ui/components/dialogs/install_dialog.py | 135 ++++++----- rare/ui/components/dialogs/install_dialog.ui | 231 ++++++++++--------- rare/utils/models.py | 2 + 6 files changed, 289 insertions(+), 245 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 9da47805..6d4c3d13 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -1,19 +1,21 @@ import os from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot -from PyQt5.QtWidgets import QDialog, QFileDialog +from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox from custom_legendary.core import LegendaryCore +from custom_legendary.utils.selective_dl import games from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.utils.extra_widgets import PathEdit +from rare.utils.models import InstallOptions from rare.utils.utils import get_size class InstallDialog(QDialog, Ui_InstallDialog): - infos = False + options = False - def __init__(self, app_name, core: LegendaryCore, update=False): - super(InstallDialog, self).__init__() + def __init__(self, app_name, core: LegendaryCore, update=False, parent=None): + super(InstallDialog, self).__init__(parent=parent) self.setupUi(self) self.core = core @@ -23,10 +25,9 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.threadpool = QThreadPool.globalInstance() - header_text = self.tr("Update") if update else self.tr("Install") - self.install_dialog_label.setText("

{} \"{}\"

".format(header_text, self.game.app_title)) - # TODO: Get application name from QApplication instance - self.setWindowTitle("{} - {} \"{}\"".format("Rare", header_text, self.game.app_title)) + header = self.tr("Update") if update else self.tr("Install") + self.install_dialog_label.setText(f"

{header} \"{self.game.app_title}\"

") + self.setWindowTitle(f"{self.windowTitle()} - {header} \"{self.game.app_title}\"") if self.core.lgd.config.has_option("Legendary", "install_dir"): default_path = self.core.lgd.config.get("Legendary", "install_dir") @@ -35,14 +36,14 @@ class InstallDialog(QDialog, Ui_InstallDialog): if not default_path: default_path = os.path.expanduser("~/legendary") - self.install_dir_path = PathEdit(text=default_path, + self.install_dir_edit = PathEdit(text=default_path, file_type=QFileDialog.DirectoryOnly, edit_func=self.on_install_dir_text_changed) - self.install_dir_layout.addWidget(self.install_dir_path) + self.install_dir_layout.addWidget(self.install_dir_edit) if update: self.install_dir_label.setVisible(False) - self.install_dir_path.setVisible(False) + self.install_dir_edit.setVisible(False) if self.core.lgd.config.has_option("Legendary", "max_workers"): max_workers = self.core.lgd.config.get("Legendary", "max_workers") @@ -50,41 +51,70 @@ class InstallDialog(QDialog, Ui_InstallDialog): max_workers = 0 self.max_workers_spin.setValue(int(max_workers)) - self.get_install_info(app_name, default_path) + self.sdl_list_checks = list() + self.tags = list() + try: + for key, info in games[app_name].items(): + cb = QDataCheckBox(info['name'], info['tags']) + cb.stateChanged.connect(self.on_sdl_checkbox_changed) + if key == '__required': + self.tags.extend(info['tags']) + cb.setChecked(True) + cb.setDisabled(True) + self.sdl_list_layout.addWidget(cb) + self.sdl_list_checks.append(cb) + self.sdl_list_frame.resize(self.sdl_list_frame.minimumSize()) + except KeyError: + self.sdl_list_frame.setVisible(False) + self.sdl_list_label.setVisible(False) + + self.get_install_info(app_name, default_path, self.tags) self.install_button.clicked.connect(self.on_install_button_clicked) self.cancel_button.clicked.connect(self.on_cancel_button_clicked) - self.resize(self.minimumSizeHint()) + self.resize(self.minimumSize()) self.setFixedSize(self.size()) - def get_information(self, path=None): + def get_install_options(self, path=None): if path: - self.install_dir_path.setText(path) + self.install_dir_edit.setText(path) self.exec_() - return self.infos + return self.options - def get_install_info(self, app_name, default_path): - updating_text = self.tr("Updating...") - self.download_size_info_label.setText(updating_text) + def get_install_info(self, app_name, path, tags): + message = self.tr("Updating...") + self.download_size_info_label.setText(message) self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") - self.install_size_info_label.setText(updating_text) + self.install_size_info_label.setText(message) self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_button.setEnabled(False) - info_worker = InstallInfoWorker(app_name, default_path, self.core) + self.sdl_list_frame.setEnabled(False) + info_worker = InstallInfoWorker(app_name, path, tags, self.core) info_worker.setAutoDelete(True) info_worker.signals.finished.connect(self.on_worker_finished) self.threadpool.start(info_worker) + def on_sdl_checkbox_changed(self): + self.tags = list() + for cb in self.sdl_list_checks: + if data := cb.isChecked(): + self.tags.extend(data) + self.get_install_info(self.app_name, self.install_dir_edit.text(), self.tags) + def on_install_dir_text_changed(self, path: str): - self.get_install_info(self.app_name, path) + self.get_install_info(self.app_name, path, self.tags) def on_install_button_clicked(self): - self.infos = self.install_dir_path.text() if not self.update_game else None, \ - self.max_workers_spin.value(), \ - self.force_download_check.isChecked(), \ - self.ignore_space_check.isChecked(), \ - self.download_only_check.isChecked() + self.options = InstallOptions( + app_name=self.app_name, + path=self.install_dir_edit.text() if not self.update_game else None, + max_workers=self.max_workers_spin.value(), + force=self.force_download_check.isChecked(), + ignore_free_space=self.ignore_space_check.isChecked(), + download_only=self.download_only_check.isChecked(), + sdl_list=self.tags + ) self.threadpool.clear() self.close() @@ -94,11 +124,9 @@ class InstallDialog(QDialog, Ui_InstallDialog): def on_worker_finished(self, info: tuple): download_size, install_size = info - # TODO: Check available size and act accordingly # TODO: (show message in label | color it | disable install unless ignore) # TODO: Find a way to get the installation size delta and show it - if download_size is not None and install_size is not None: if download_size: self.download_size_info_label.setText("{}".format(get_size(download_size))) @@ -112,6 +140,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): else: self.download_size_info_label.setText("Error") self.install_size_info_label.setText("Error") + self.sdl_list_frame.setEnabled(True) class InstallInfoWorkerSignals(QObject): @@ -120,12 +149,13 @@ class InstallInfoWorkerSignals(QObject): class InstallInfoWorker(QRunnable): - def __init__(self, app_name, path, core: LegendaryCore): + def __init__(self, app_name, path, tags, core: LegendaryCore): super(InstallInfoWorker, self).__init__() self.signals = InstallInfoWorkerSignals() self.core = core self.app_name = app_name self.path = path + self.tags = tags @pyqtSlot() def run(self): @@ -133,8 +163,23 @@ class InstallInfoWorker(QRunnable): dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( app_name=self.app_name, base_path=self.path, + sdl_prompt=lambda app_name, title: self.tags ) self.signals.finished.emit((analysis.dl_size, analysis.install_size)) except: self.signals.finished.emit((None, None)) return + + +class QDataCheckBox(QCheckBox): + + def __init__(self, text, data=None): + super(QDataCheckBox, self).__init__() + self.setText(text) + self.data = data + + def isChecked(self): + if super(QDataCheckBox, self).isChecked(): + return self.data + else: + return False diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index 5818536e..9c1da44c 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -97,12 +97,9 @@ class TabWidget(QTabWidget): def install_game(self, app_name, disable_path=False): - infos = InstallDialog(app_name, self.core, disable_path).get_information() - if infos: - path, max_workers, force, ignore_free_space, dl_only = infos - options = InstallOptions(app_name=app_name, max_workers=max_workers, path=path, force=force, - ignore_free_space=ignore_free_space, download_only=dl_only) - + options = InstallDialog(app_name, self.core, disable_path, parent=self).get_install_options() + if options: + self.setCurrentIndex(1) self.start_download(options) def start_download(self, options): diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 78cf3fbc..3eaf7aae 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -29,6 +29,7 @@ class DownloadTab(QWidget): self.core = core self.layout = QVBoxLayout() self.active_game: Game = None + self.analysis = None self.info_layout = QGridLayout() @@ -85,7 +86,7 @@ class DownloadTab(QWidget): widget = UpdateWidget(self.core, igame, self) self.update_layout.addWidget(widget) self.update_widgets[igame.app_name] = widget - widget.update.connect(self.update_game) + widget.update_signal.connect(self.update_game) if QSettings().value("auto_update", False, bool): self.update_game(igame.app_name, True) widget.update_button.setDisabled(True) @@ -125,7 +126,8 @@ class DownloadTab(QWidget): # disable_delta=, # override_delta_manifest=, # reset_sdl=, - sdl_prompt=self.sdl_prompt) + sdl_prompt=lambda app_name, title: options.sdl_list + ) except Exception as e: QMessageBox.warning(self, self.tr("Error preparing download"), str(e)) @@ -153,44 +155,6 @@ class DownloadTab(QWidget): self.analysis = analysis self.installing_game.setText(self.tr("Installing Game: ") + self.active_game.app_title) - def sdl_prompt(self, app_name: str = '', title: str = '') -> list: - sdl = QDialog() - sdl.setWindowTitle('Select Additional Downloads') - - layout = QVBoxLayout(sdl) - sdl.setLayout(layout) - - pack_list = QListWidget() - layout.addWidget(pack_list) - - done = QPushButton(text='Done') - done.clicked.connect(sdl.accept) - layout.addWidget(done) - - tags = [''] - if '__required' in games[app_name]: - tags.extend(games[app_name]['__required']['tags']) - - # add available additional downloads to list - pack_list.addItems([tag + ': ' + info['name'] for tag, info in games[app_name].items() if tag != '__required']) - - # enable checkboxes - for i in range(len(pack_list)): - item = pack_list.item(i) - item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) - item.setCheckState(Qt.Unchecked) - - sdl.exec_() - - # read checkboxes states - for i in range(len(pack_list)): - item = pack_list.item(i) - if item.checkState() == Qt.Checked: - tag = item.text().split(':')[0] - tags.extend(games[app_name][tag]['tags']) - - return tags - def status(self, text): if text == "dl_finished": pass @@ -270,14 +234,12 @@ class DownloadTab(QWidget): def update_game(self, app_name: str, auto=False): logger.info("Update " + app_name) if not auto: - infos = InstallDialog(app_name, self.core, True).get_information() + options = InstallDialog(app_name, self.core, True).get_install_options() else: self.install_game(InstallOptions(app_name=app_name), True) return - if infos: - path, max_workers, force, ignore_free_space, dl_only = infos - self.install_game(InstallOptions(app_name=app_name, max_workers=max_workers, path=path, - force=force, ignore_free_space=ignore_free_space, download_only=dl_only), True) + if options: + self.install_game(options, True) else: self.update_widgets[app_name].update_button.setDisabled(False) self.update_widgets[app_name].update_with_settings.setDisabled(False) diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index e693ce8c..467d4f4c 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -14,106 +14,119 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_InstallDialog(object): def setupUi(self, InstallDialog): InstallDialog.setObjectName("InstallDialog") + InstallDialog.setWindowTitle("Rare") self.install_dialog_layout = QtWidgets.QGridLayout(InstallDialog) self.install_dialog_layout.setObjectName("install_dialog_layout") - self.ignore_space_label = QtWidgets.QLabel(InstallDialog) - self.ignore_space_label.setObjectName("ignore_space_label") - self.install_dialog_layout.addWidget(self.ignore_space_label, 4, 0, 1, 1, QtCore.Qt.AlignRight) - self.install_dialog_label = QtWidgets.QLabel(InstallDialog) - self.install_dialog_label.setObjectName("install_dialog_label") - self.install_dialog_layout.addWidget(self.install_dialog_label, 0, 0, 1, 3) - self.download_size_label = QtWidgets.QLabel(InstallDialog) - self.download_size_label.setObjectName("download_size_label") - self.install_dialog_layout.addWidget(self.download_size_label, 6, 0, 1, 1, QtCore.Qt.AlignRight) - self.install_size_info_label = QtWidgets.QLabel(InstallDialog) - self.install_size_info_label.setText("") - self.install_size_info_label.setObjectName("install_size_info_label") - self.install_dialog_layout.addWidget(self.install_size_info_label, 7, 1, 1, 2) - self.install_dir_label = QtWidgets.QLabel(InstallDialog) - self.install_dir_label.setObjectName("install_dir_label") - self.install_dialog_layout.addWidget(self.install_dir_label, 1, 0, 1, 1, QtCore.Qt.AlignRight) - spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.install_dialog_layout.addItem(spacerItem, 3, 2, 1, 1) - self.ignore_space_info_label = QtWidgets.QLabel(InstallDialog) - font = QtGui.QFont() - font.setItalic(True) - self.ignore_space_info_label.setFont(font) - self.ignore_space_info_label.setObjectName("ignore_space_info_label") - self.install_dialog_layout.addWidget(self.ignore_space_info_label, 4, 2, 1, 1) - self.force_download_check = QtWidgets.QCheckBox(InstallDialog) - self.force_download_check.setObjectName("force_download_check") - self.install_dialog_layout.addWidget(self.force_download_check, 3, 1, 1, 1) self.force_download_label = QtWidgets.QLabel(InstallDialog) self.force_download_label.setObjectName("force_download_label") self.install_dialog_layout.addWidget(self.force_download_label, 3, 0, 1, 1, QtCore.Qt.AlignRight) self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.setObjectName("button_layout") - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.button_layout.addItem(spacerItem1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.button_layout.addItem(spacerItem) self.cancel_button = QtWidgets.QPushButton(InstallDialog) self.cancel_button.setObjectName("cancel_button") self.button_layout.addWidget(self.cancel_button) self.install_button = QtWidgets.QPushButton(InstallDialog) self.install_button.setObjectName("install_button") self.button_layout.addWidget(self.install_button) - self.install_dialog_layout.addLayout(self.button_layout, 8, 0, 1, 3) - self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog) - self.max_workers_spin.setObjectName("max_workers_spin") - self.install_dialog_layout.addWidget(self.max_workers_spin, 2, 1, 1, 1) + self.install_dialog_layout.addLayout(self.button_layout, 9, 0, 1, 3) + self.ignore_space_info_label = QtWidgets.QLabel(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.ignore_space_info_label.setFont(font) + self.ignore_space_info_label.setObjectName("ignore_space_info_label") + self.install_dialog_layout.addWidget(self.ignore_space_info_label, 4, 2, 1, 1) + self.install_dir_label = QtWidgets.QLabel(InstallDialog) + self.install_dir_label.setObjectName("install_dir_label") + self.install_dialog_layout.addWidget(self.install_dir_label, 1, 0, 1, 1, QtCore.Qt.AlignRight) self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog) self.ignore_space_check.setObjectName("ignore_space_check") self.install_dialog_layout.addWidget(self.ignore_space_check, 4, 1, 1, 1) - self.install_size_label = QtWidgets.QLabel(InstallDialog) - self.install_size_label.setObjectName("install_size_label") - self.install_dialog_layout.addWidget(self.install_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight) self.max_workers_info_label = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) self.max_workers_info_label.setFont(font) self.max_workers_info_label.setObjectName("max_workers_info_label") self.install_dialog_layout.addWidget(self.max_workers_info_label, 2, 2, 1, 1) - self.install_dir_layout = QtWidgets.QHBoxLayout() - self.install_dir_layout.setObjectName("install_dir_layout") - self.install_dialog_layout.addLayout(self.install_dir_layout, 1, 1, 1, 2) - self.download_size_info_label = QtWidgets.QLabel(InstallDialog) - self.download_size_info_label.setText("") - self.download_size_info_label.setObjectName("download_size_info_label") - self.install_dialog_layout.addWidget(self.download_size_info_label, 6, 1, 1, 2) - self.max_workers_label = QtWidgets.QLabel(InstallDialog) - self.max_workers_label.setObjectName("max_workers_label") - self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) - self.download_only_label = QtWidgets.QLabel(InstallDialog) - self.download_only_label.setObjectName("download_only_label") - self.install_dialog_layout.addWidget(self.download_only_label, 5, 0, 1, 1, QtCore.Qt.AlignRight) - self.download_only_check = QtWidgets.QCheckBox(InstallDialog) - self.download_only_check.setText("") - self.download_only_check.setObjectName("download_only_check") - self.install_dialog_layout.addWidget(self.download_only_check, 5, 1, 1, 1) + self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog) + self.max_workers_spin.setObjectName("max_workers_spin") + self.install_dialog_layout.addWidget(self.max_workers_spin, 2, 1, 1, 1) self.download_only_info_label = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) self.download_only_info_label.setFont(font) self.download_only_info_label.setObjectName("download_only_info_label") self.install_dialog_layout.addWidget(self.download_only_info_label, 5, 2, 1, 1) + self.install_size_label = QtWidgets.QLabel(InstallDialog) + self.install_size_label.setObjectName("install_size_label") + self.install_dialog_layout.addWidget(self.install_size_label, 8, 0, 1, 1, QtCore.Qt.AlignRight) + self.install_dir_layout = QtWidgets.QHBoxLayout() + self.install_dir_layout.setObjectName("install_dir_layout") + self.install_dialog_layout.addLayout(self.install_dir_layout, 1, 1, 1, 2) + spacerItem1 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.install_dialog_layout.addItem(spacerItem1, 3, 2, 1, 1) + self.ignore_space_label = QtWidgets.QLabel(InstallDialog) + self.ignore_space_label.setObjectName("ignore_space_label") + self.install_dialog_layout.addWidget(self.ignore_space_label, 4, 0, 1, 1, QtCore.Qt.AlignRight) + self.download_only_label = QtWidgets.QLabel(InstallDialog) + self.download_only_label.setObjectName("download_only_label") + self.install_dialog_layout.addWidget(self.download_only_label, 5, 0, 1, 1, QtCore.Qt.AlignRight) + self.max_workers_label = QtWidgets.QLabel(InstallDialog) + self.max_workers_label.setObjectName("max_workers_label") + self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) + self.install_size_info_label = QtWidgets.QLabel(InstallDialog) + self.install_size_info_label.setText("") + self.install_size_info_label.setObjectName("install_size_info_label") + self.install_dialog_layout.addWidget(self.install_size_info_label, 8, 1, 1, 2) + self.download_size_label = QtWidgets.QLabel(InstallDialog) + self.download_size_label.setObjectName("download_size_label") + self.install_dialog_layout.addWidget(self.download_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight) + self.download_size_info_label = QtWidgets.QLabel(InstallDialog) + self.download_size_info_label.setText("") + self.download_size_info_label.setObjectName("download_size_info_label") + self.install_dialog_layout.addWidget(self.download_size_info_label, 7, 1, 1, 2) + self.force_download_check = QtWidgets.QCheckBox(InstallDialog) + self.force_download_check.setObjectName("force_download_check") + self.install_dialog_layout.addWidget(self.force_download_check, 3, 1, 1, 1) + self.install_dialog_label = QtWidgets.QLabel(InstallDialog) + self.install_dialog_label.setObjectName("install_dialog_label") + self.install_dialog_layout.addWidget(self.install_dialog_label, 0, 0, 1, 3) + self.download_only_check = QtWidgets.QCheckBox(InstallDialog) + self.download_only_check.setText("") + self.download_only_check.setObjectName("download_only_check") + self.install_dialog_layout.addWidget(self.download_only_check, 5, 1, 1, 1) + self.sdl_list_frame = QtWidgets.QFrame(InstallDialog) + self.sdl_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.sdl_list_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.sdl_list_frame.setObjectName("sdl_list_frame") + self.sdl_list_layout = QtWidgets.QVBoxLayout(self.sdl_list_frame) + self.sdl_list_layout.setSpacing(0) + self.sdl_list_layout.setObjectName("sdl_list_layout") + self.install_dialog_layout.addWidget(self.sdl_list_frame, 6, 1, 1, 2, QtCore.Qt.AlignTop) + self.sdl_list_label = QtWidgets.QLabel(InstallDialog) + self.sdl_list_label.setObjectName("sdl_list_label") + self.install_dialog_layout.addWidget(self.sdl_list_label, 6, 0, 1, 1, QtCore.Qt.AlignRight) self.retranslateUi(InstallDialog) QtCore.QMetaObject.connectSlotsByName(InstallDialog) def retranslateUi(self, InstallDialog): _translate = QtCore.QCoreApplication.translate - self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) - self.install_dialog_label.setText(_translate("InstallDialog", "error")) - self.download_size_label.setText(_translate("InstallDialog", "Download size")) - self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) - self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!")) self.force_download_label.setText(_translate("InstallDialog", "Force download")) self.cancel_button.setText(_translate("InstallDialog", "Cancel")) self.install_button.setText(_translate("InstallDialog", "Install")) - self.install_size_label.setText(_translate("InstallDialog", "Total install size")) + self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!")) + self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)")) - self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) - self.download_only_label.setText(_translate("InstallDialog", "Download only")) self.download_only_info_label.setText(_translate("InstallDialog", "Do not try to install.")) + self.install_size_label.setText(_translate("InstallDialog", "Total install size")) + self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) + self.download_only_label.setText(_translate("InstallDialog", "Download only")) + self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) + self.download_size_label.setText(_translate("InstallDialog", "Download size")) + self.install_dialog_label.setText(_translate("InstallDialog", "error")) + self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs")) if __name__ == "__main__": diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index 576482dd..b8a26ba1 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -2,70 +2,10 @@ InstallDialog + + Rare + - - - - Ignore free space - - - - - - - error - - - - - - - Download size - - - - - - - - - - - - - - Install directory - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - true - - - - Use with caution! - - - - - - @@ -73,7 +13,7 @@ - + @@ -104,19 +44,28 @@ - - + + + + + true + + + + Use with caution! + + + + + + + Install directory + + - - - - Total install size - - - @@ -129,36 +78,8 @@ - - - - - - - - - - - - - - Max workers - - - - - - - Download only - - - - - - - - - + + @@ -172,6 +93,110 @@ + + + + Total install size + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Ignore free space + + + + + + + Download only + + + + + + + Max workers + + + + + + + + + + + + + + Download size + + + + + + + + + + + + + + + + + error + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + + + + + + Optional packs + + + diff --git a/rare/utils/models.py b/rare/utils/models.py index 503d0532..5cbf8af3 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -5,6 +5,7 @@ class InstallOptions: def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), max_workers: int = os.cpu_count() * 2, repair: bool = False, download_only: bool = False, ignore_free_space: bool = False, force: bool = False, + sdl_list: list = list() ): self.app_name = app_name self.path = path @@ -13,3 +14,4 @@ class InstallOptions: self.download_only = download_only self.ignore_free_space = ignore_free_space self.force = force + self.sdl_list = sdl_list From eb461529f7f7b64467d0dd22cd9fb1b11c64f5ef Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Fri, 21 May 2021 21:54:26 +0300 Subject: [PATCH 10/22] While we are here, adjust UninstalledInfo to match GameInfo. Make it easier to unify them in the future. --- .../tabs/games/game_info/__init__.py | 25 +- .../tabs/games/game_info/uninstalled_info.py | 90 +-- .../tabs/games/game_info/game_info.py | 112 +-- .../tabs/games/game_info/game_info.ui | 756 +++++++++--------- 4 files changed, 497 insertions(+), 486 deletions(-) diff --git a/rare/components/tabs/games/game_info/__init__.py b/rare/components/tabs/games/game_info/__init__.py index 465fd8b6..b9729dfa 100644 --- a/rare/components/tabs/games/game_info/__init__.py +++ b/rare/components/tabs/games/game_info/__init__.py @@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QWidget, QTabWidget, QMessageBox from qtawesome import icon from custom_legendary.core import LegendaryCore -from custom_legendary.models.game import InstalledGame, Game +from custom_legendary.models.game import Game, InstalledGame 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 @@ -65,22 +65,26 @@ class GameInfo(QWidget, Ui_GameInfo): def __init__(self, core: LegendaryCore, parent): super(GameInfo, self).__init__(parent=parent) self.setupUi(self) - self.ratings = {"platinum": self.tr("Platimum"), + 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 from ProtonDB"), - "pending": "Not enough reports"} + "fail": self.tr("Could not get grade"), + "pending": self.tr("Not enough reports")} if os.path.exists(p := os.path.expanduser("~/.cache/rare/game_list.json")): self.grade_table = json.load(open(p)) else: self.grade_table = {} - self.widget = QWidget() - self.core = core + if os.name == "nt": self.lbl_grade.setVisible(False) self.grade.setVisible(False) + self.game_actions_stack.setCurrentIndex(0) + self.game_actions_stack.resize(self.game_actions_stack.minimumSize()) + self.uninstall_button.clicked.connect(self.uninstall) self.verify_button.clicked.connect(self.verify) self.repair_button.clicked.connect(self.repair) @@ -153,14 +157,15 @@ class GameInfo(QWidget, Ui_GameInfo): if os.name != "nt" and self.grade_table: try: - grade = self.ratings.get(self.grade_table[app_name].get("grade")) + grade = self.grade_table[app_name]["grade"] except KeyError: - grade = (self.tr("Error")) - self.grade.setText(grade) + grade = "fail" + self.grade.setText(self.ratings[grade]) if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(app_name): self.verify_widget.setCurrentIndex(0) elif self.verify_threads.get(app_name): self.verify_widget.setCurrentIndex(1) self.verify_progress.setValue( - self.verify_threads[app_name].num / self.verify_threads[app_name].total * 100) + self.verify_threads[app_name].num / self.verify_threads[app_name].total * 100 + ) diff --git a/rare/components/tabs/games/game_info/uninstalled_info.py b/rare/components/tabs/games/game_info/uninstalled_info.py index fcb5b781..8a230516 100644 --- a/rare/components/tabs/games/game_info/uninstalled_info.py +++ b/rare/components/tabs/games/game_info/uninstalled_info.py @@ -1,15 +1,17 @@ import json import os -from PyQt5.QtCore import pyqtSignal, QSettings, Qt +from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QPixmap, QKeyEvent -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QTreeView +from PyQt5.QtWidgets import QWidget, QTabWidget, QTreeView from qtawesome import icon from custom_legendary.core import LegendaryCore from custom_legendary.models.game import Game +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 +from rare.utils.utils import IMAGE_DIR class UninstalledTabInfo(QTabWidget): @@ -18,7 +20,6 @@ class UninstalledTabInfo(QTabWidget): self.app_name = "" self.core = core self.setTabBar(SideTabBar()) - self.setTabPosition(QTabWidget.West) self.addTab(QWidget(), icon("mdi.keyboard-backspace", color="white"), self.tr("Back")) @@ -49,69 +50,44 @@ class UninstalledTabInfo(QTabWidget): self.parent().layout.setCurrentIndex(0) -class UninstalledInfo(QWidget): +class UninstalledInfo(QWidget, Ui_GameInfo): game: Game install_game = pyqtSignal(str) - def __init__(self, core: LegendaryCore, parent): + def __init__(self, core: LegendaryCore, parent=None): super(UninstalledInfo, self).__init__(parent=parent) - self.layout = QVBoxLayout() - - if os.path.exists(p := os.path.expanduser("~/.cache/rare/game_list.json")): - self.grade_table = json.load(open(p)) - else: - self.grade_table = {} + 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 from ProtonDB"), - "pending": "Not enough reports"} + "fail": self.tr("Could not get grade"), + "pending": self.tr("Not enough reports")} + if os.path.exists(p := os.path.expanduser("~/.cache/rare/game_list.json")): + self.grade_table = json.load(open(p)) + else: + self.grade_table = {} - self.core = core + if os.name == "nt": + self.lbl_grade.setVisible(False) + self.grade.setVisible(False) - self.settings = QSettings() + self.install_size.setEnabled(False) + self.lbl_install_size.setEnabled(False) + self.install_path.setEnabled(False) + self.lbl_install_path.setEnabled(False) - self.top_layout = QHBoxLayout() - left_layout = QVBoxLayout() - self.image = QLabel() - left_layout.addWidget(self.image) - left_layout.addStretch(1) - self.top_layout.addLayout(left_layout) - self.right_layout = QVBoxLayout() + self.game_actions_stack.setCurrentIndex(1) + self.game_actions_stack.resize(self.game_actions_stack.minimumSize()) - self.title = QLabel("Error") - self.right_layout.addWidget(self.title) - - self.app_name = QLabel("Error") - self.right_layout.addWidget(self.app_name) - if os.name != "nt": - self.rating = QLabel("Rating: Error") - self.right_layout.addWidget(self.rating) - - self.install_button = QPushButton(self.tr("Install")) - self.install_button.setFixedWidth(300) - self.install_button.setStyleSheet("""background-color: #090""") self.install_button.clicked.connect(lambda: self.install_game.emit(self.game.app_name)) - self.right_layout.addWidget(self.install_button) - self.version = QLabel("Error") - self.right_layout.addWidget(self.version) - self.right_layout.addStretch(1) - self.top_layout.addLayout(self.right_layout) - - self.top_layout.addStretch(1) - self.layout.addLayout(self.top_layout) - - self.setLayout(self.layout) def update_game(self, app_name): self.game = self.core.get_game(app_name) - self.title.setText(f"

{self.game.app_title}

") - self.app_name.setText("Appname: " + app_name) - - IMAGE_DIR = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images"), str) + self.game_title.setText(f"

{self.game.app_title}

") if os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png"): pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png") @@ -127,13 +103,15 @@ class UninstalledInfo(QWidget): pixmap = pixmap.scaled(w, int(w * 4 / 3)) self.image.setPixmap(pixmap) - self.version.setText(self.game.asset_info.build_version) - if self.grade_table and (not os.name == "nt"): + self.app_name.setText(self.game.app_name) + self.version.setText(self.game.app_version) + self.dev.setText(self.game.metadata["developer"]) + self.install_size.setText("N/A") + self.install_path.setText("N/A") + + if os.name != "nt" and self.grade_table: try: - rating = self.grade_table[app_name]["grade"] + grade = self.grade_table[app_name]["grade"] except KeyError: - rating = "fail" - if rating not in ["fail", "pending"]: - self.rating.setText(self.tr("Rating from ProtonDB: ") + self.ratings[rating]) - else: - self.rating.setText(self.ratings[rating]) + grade = "fail" + self.grade.setText(self.ratings[grade]) diff --git a/rare/ui/components/tabs/games/game_info/game_info.py b/rare/ui/components/tabs/games/game_info/game_info.py index 11f5b1d9..d320f7a9 100644 --- a/rare/ui/components/tabs/games/game_info/game_info.py +++ b/rare/ui/components/tabs/games/game_info/game_info.py @@ -22,7 +22,7 @@ class Ui_GameInfo(object): self.layout_game_info_form.setObjectName("layout_game_info_form") self.install_size = QtWidgets.QLabel(GameInfo) self.install_size.setText("error") - self.install_size.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.install_size.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.install_size.setObjectName("install_size") self.layout_game_info_form.addWidget(self.install_size, 4, 1, 1, 1) self.lbl_dev = QtWidgets.QLabel(GameInfo) @@ -39,7 +39,7 @@ class Ui_GameInfo(object): self.layout_game_info_form.addWidget(self.lbl_dev, 0, 0, 1, 1, QtCore.Qt.AlignRight) self.version = QtWidgets.QLabel(GameInfo) self.version.setText("error") - self.version.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.version.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.version.setObjectName("version") self.layout_game_info_form.addWidget(self.version, 2, 1, 1, 1) self.lbl_install_path = QtWidgets.QLabel(GameInfo) @@ -70,43 +70,6 @@ class Ui_GameInfo(object): self.layout_game_info_form.addItem(spacerItem, 7, 1, 1, 1) spacerItem1 = QtWidgets.QSpacerItem(20, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.layout_game_info_form.addItem(spacerItem1, 7, 0, 1, 1) - self.wg_game_actions = QtWidgets.QWidget(GameInfo) - self.wg_game_actions.setMinimumSize(QtCore.QSize(250, 0)) - self.wg_game_actions.setObjectName("wg_game_actions") - self.layout_game_actions = QtWidgets.QVBoxLayout(self.wg_game_actions) - self.layout_game_actions.setContentsMargins(0, 0, 0, 0) - self.layout_game_actions.setObjectName("layout_game_actions") - self.uninstall_button = QtWidgets.QPushButton(self.wg_game_actions) - self.uninstall_button.setObjectName("uninstall_button") - self.layout_game_actions.addWidget(self.uninstall_button) - self.verify_widget = QtWidgets.QStackedWidget(self.wg_game_actions) - self.verify_widget.setObjectName("verify_widget") - self.page_verify_button = QtWidgets.QWidget() - self.page_verify_button.setObjectName("page_verify_button") - self.layout_verify_button = QtWidgets.QVBoxLayout(self.page_verify_button) - self.layout_verify_button.setContentsMargins(0, 0, 0, 0) - self.layout_verify_button.setSpacing(0) - self.layout_verify_button.setObjectName("layout_verify_button") - self.verify_button = QtWidgets.QPushButton(self.page_verify_button) - self.verify_button.setObjectName("verify_button") - self.layout_verify_button.addWidget(self.verify_button) - self.verify_widget.addWidget(self.page_verify_button) - self.page_verify_progress = QtWidgets.QWidget() - self.page_verify_progress.setObjectName("page_verify_progress") - self.layout_verify_progress = QtWidgets.QVBoxLayout(self.page_verify_progress) - self.layout_verify_progress.setContentsMargins(0, 0, 0, 0) - self.layout_verify_progress.setSpacing(0) - self.layout_verify_progress.setObjectName("layout_verify_progress") - self.verify_progress = QtWidgets.QProgressBar(self.page_verify_progress) - self.verify_progress.setProperty("value", 24) - self.verify_progress.setObjectName("verify_progress") - self.layout_verify_progress.addWidget(self.verify_progress) - self.verify_widget.addWidget(self.page_verify_progress) - self.layout_game_actions.addWidget(self.verify_widget) - self.repair_button = QtWidgets.QPushButton(self.wg_game_actions) - self.repair_button.setObjectName("repair_button") - self.layout_game_actions.addWidget(self.repair_button) - self.layout_game_info_form.addWidget(self.wg_game_actions, 6, 1, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.lbl_version = QtWidgets.QLabel(GameInfo) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -133,18 +96,18 @@ class Ui_GameInfo(object): self.layout_game_info_form.addWidget(self.lbl_app_name, 1, 0, 1, 1, QtCore.Qt.AlignRight) self.dev = QtWidgets.QLabel(GameInfo) self.dev.setText("error") - self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.dev.setObjectName("dev") self.layout_game_info_form.addWidget(self.dev, 0, 1, 1, 1) self.app_name = QtWidgets.QLabel(GameInfo) self.app_name.setText("error") - self.app_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.app_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.app_name.setObjectName("app_name") self.layout_game_info_form.addWidget(self.app_name, 1, 1, 1, 1) self.install_path = QtWidgets.QLabel(GameInfo) self.install_path.setText("error") self.install_path.setWordWrap(True) - self.install_path.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.install_path.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.install_path.setObjectName("install_path") self.layout_game_info_form.addWidget(self.install_path, 5, 1, 1, 1) self.lbl_game_actions = QtWidgets.QLabel(GameInfo) @@ -168,23 +131,74 @@ class Ui_GameInfo(object): self.layout_game_info_form.addWidget(self.lbl_grade, 3, 0, 1, 1, QtCore.Qt.AlignRight) self.grade = QtWidgets.QLabel(GameInfo) self.grade.setText("error") - self.grade.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.grade.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.grade.setObjectName("grade") self.layout_game_info_form.addWidget(self.grade, 3, 1, 1, 1) + self.game_actions_stack = QtWidgets.QStackedWidget(GameInfo) + self.game_actions_stack.setMinimumSize(QtCore.QSize(250, 0)) + self.game_actions_stack.setObjectName("game_actions_stack") + self.installed_page = QtWidgets.QWidget() + self.installed_page.setObjectName("installed_page") + self.installed_layout = QtWidgets.QVBoxLayout(self.installed_page) + self.installed_layout.setContentsMargins(0, 0, 0, 0) + self.installed_layout.setObjectName("installed_layout") + self.verify_widget = QtWidgets.QStackedWidget(self.installed_page) + self.verify_widget.setObjectName("verify_widget") + self.page_verify_button = QtWidgets.QWidget() + self.page_verify_button.setObjectName("page_verify_button") + self.layout_verify_button = QtWidgets.QVBoxLayout(self.page_verify_button) + self.layout_verify_button.setContentsMargins(0, 0, 0, 0) + self.layout_verify_button.setSpacing(0) + self.layout_verify_button.setObjectName("layout_verify_button") + self.verify_button = QtWidgets.QPushButton(self.page_verify_button) + self.verify_button.setObjectName("verify_button") + self.layout_verify_button.addWidget(self.verify_button) + self.verify_widget.addWidget(self.page_verify_button) + self.page_verify_progress = QtWidgets.QWidget() + self.page_verify_progress.setObjectName("page_verify_progress") + self.layout_verify_progress = QtWidgets.QVBoxLayout(self.page_verify_progress) + self.layout_verify_progress.setContentsMargins(0, 0, 0, 0) + self.layout_verify_progress.setSpacing(0) + self.layout_verify_progress.setObjectName("layout_verify_progress") + self.verify_progress = QtWidgets.QProgressBar(self.page_verify_progress) + self.verify_progress.setProperty("value", 24) + self.verify_progress.setObjectName("verify_progress") + self.layout_verify_progress.addWidget(self.verify_progress) + self.verify_widget.addWidget(self.page_verify_progress) + self.installed_layout.addWidget(self.verify_widget) + self.repair_button = QtWidgets.QPushButton(self.installed_page) + self.repair_button.setObjectName("repair_button") + self.installed_layout.addWidget(self.repair_button) + self.uninstall_button = QtWidgets.QPushButton(self.installed_page) + self.uninstall_button.setStyleSheet("background-color: #900") + self.uninstall_button.setObjectName("uninstall_button") + self.installed_layout.addWidget(self.uninstall_button) + self.game_actions_stack.addWidget(self.installed_page) + self.uninstalled_page = QtWidgets.QWidget() + self.uninstalled_page.setObjectName("uninstalled_page") + self.uninstalled_layout = QtWidgets.QVBoxLayout(self.uninstalled_page) + self.uninstalled_layout.setObjectName("uninstalled_layout") + self.install_button = QtWidgets.QPushButton(self.uninstalled_page) + self.install_button.setStyleSheet("background-color: #090") + self.install_button.setObjectName("install_button") + self.uninstalled_layout.addWidget(self.install_button) + self.game_actions_stack.addWidget(self.uninstalled_page) + self.layout_game_info_form.addWidget(self.game_actions_stack, 6, 1, 1, 1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.layout_game_info.addLayout(self.layout_game_info_form, 2, 1, 1, 1) self.image = QtWidgets.QLabel(GameInfo) self.image.setFrameShape(QtWidgets.QFrame.StyledPanel) self.image.setFrameShadow(QtWidgets.QFrame.Sunken) self.image.setText("") self.image.setObjectName("image") - self.layout_game_info.addWidget(self.image, 2, 0, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) + self.layout_game_info.addWidget(self.image, 2, 0, 1, 1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.game_title = QtWidgets.QLabel(GameInfo) self.game_title.setText("error") - self.game_title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) + self.game_title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.game_title.setObjectName("game_title") self.layout_game_info.addWidget(self.game_title, 0, 0, 1, 3) self.retranslateUi(GameInfo) + self.game_actions_stack.setCurrentIndex(0) self.verify_widget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(GameInfo) @@ -194,18 +208,18 @@ class Ui_GameInfo(object): self.lbl_dev.setText(_translate("GameInfo", "Developer")) self.lbl_install_path.setText(_translate("GameInfo", "Installation Path")) self.lbl_install_size.setText(_translate("GameInfo", "Installation Size")) - self.uninstall_button.setText(_translate("GameInfo", "Uninstall Game")) - self.verify_button.setText(_translate("GameInfo", "Verify Installation")) - self.repair_button.setText(_translate("GameInfo", "Repair Instalation")) self.lbl_version.setText(_translate("GameInfo", "Version")) self.lbl_app_name.setText(_translate("GameInfo", "Application Name")) self.lbl_game_actions.setText(_translate("GameInfo", "Actions")) self.lbl_grade.setText(_translate("GameInfo", "ProtonDB Grade")) + self.verify_button.setText(_translate("GameInfo", "Verify Installation")) + self.repair_button.setText(_translate("GameInfo", "Repair Instalation")) + self.uninstall_button.setText(_translate("GameInfo", "Uninstall Game")) + self.install_button.setText(_translate("GameInfo", "Install Game")) if __name__ == "__main__": import sys - app = QtWidgets.QApplication(sys.argv) GameInfo = QtWidgets.QWidget() ui = Ui_GameInfo() diff --git a/rare/ui/components/tabs/games/game_info/game_info.ui b/rare/ui/components/tabs/games/game_info/game_info.ui index 1d1926b6..edec48ef 100644 --- a/rare/ui/components/tabs/games/game_info/game_info.ui +++ b/rare/ui/components/tabs/games/game_info/game_info.ui @@ -1,376 +1,390 @@ - GameInfo - - - - 0 - 0 - 436 - 317 - - - - Game Info - - - - - - 6 - - - 6 - - - 6 - - - 6 - - - 12 - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Developer - - - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Installation Path - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Installation Size - - - - - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 250 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Uninstall Game - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Verify Installation - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 24 - - - - - - - - - - - Repair Instalation - - - - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Version - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Application Name - - - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - error - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Actions - - - - - - - - 75 - true - - - - ProtonDB Grade - - - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - - - - - - - error - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - + GameInfo + + + Game Info + + + + + + 6 + + + 6 + + + 6 + + + 6 + + + 12 + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Developer + + + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Installation Path + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Installation Size + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Version + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Application Name + + + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + error + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Actions + + + + + + + + 75 + true + + + + ProtonDB Grade + + + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 250 + 0 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Verify Installation + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 24 + + + + + + + + + + + Repair Instalation + + + + + + + background-color: #900 + + + Uninstall Game + + + + + + + + + + background-color: #090 + + + Install Game + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + - - + + + + + error + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + From 494e07852290590fb3137a34bbd82027615403be Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sat, 22 May 2021 20:52:58 +0300 Subject: [PATCH 11/22] Fix remaining issues with the install dialog. * Fix race condition between the sdl checkboxes. * Fix default tag not been included in the list of tags. * Delete InstallDialog on close. * Specify parent for InstallDialog. --- rare/components/dialogs/install_dialog.py | 12 +++++++----- rare/components/tab_widget.py | 4 ++-- rare/components/tabs/downloads/__init__.py | 3 ++- rare/utils/models.py | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 6d4c3d13..097d4994 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -1,6 +1,6 @@ import os -from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot +from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox from custom_legendary.core import LegendaryCore @@ -15,7 +15,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): options = False def __init__(self, app_name, core: LegendaryCore, update=False, parent=None): - super(InstallDialog, self).__init__(parent=parent) + super(InstallDialog, self).__init__(parent) + self.setAttribute(Qt.WA_DeleteOnClose, True) self.setupUi(self) self.core = core @@ -52,11 +53,10 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.max_workers_spin.setValue(int(max_workers)) self.sdl_list_checks = list() - self.tags = list() + self.tags = [''] try: for key, info in games[app_name].items(): cb = QDataCheckBox(info['name'], info['tags']) - cb.stateChanged.connect(self.on_sdl_checkbox_changed) if key == '__required': self.tags.extend(info['tags']) cb.setChecked(True) @@ -64,6 +64,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.sdl_list_layout.addWidget(cb) self.sdl_list_checks.append(cb) self.sdl_list_frame.resize(self.sdl_list_frame.minimumSize()) + for cb in self.sdl_list_checks: + cb.stateChanged.connect(self.on_sdl_checkbox_changed) except KeyError: self.sdl_list_frame.setVisible(False) self.sdl_list_label.setVisible(False) @@ -96,7 +98,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.threadpool.start(info_worker) def on_sdl_checkbox_changed(self): - self.tags = list() + self.tags = [''] for cb in self.sdl_list_checks: if data := cb.isChecked(): self.tags.extend(data) diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index 9c1da44c..97aea35e 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -96,8 +96,8 @@ class TabWidget(QTabWidget): self.setIconSize(QSize(25, 25)) def install_game(self, app_name, disable_path=False): - - options = InstallDialog(app_name, self.core, disable_path, parent=self).get_install_options() + dialog = InstallDialog(app_name, self.core, disable_path, parent=self) + options = dialog.get_install_options() if options: self.setCurrentIndex(1) self.start_download(options) diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 3eaf7aae..edfba057 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -234,7 +234,8 @@ class DownloadTab(QWidget): def update_game(self, app_name: str, auto=False): logger.info("Update " + app_name) if not auto: - options = InstallDialog(app_name, self.core, True).get_install_options() + dialog = InstallDialog(app_name, self.core, update=True, parent=self) + options = dialog.get_install_options() else: self.install_game(InstallOptions(app_name=app_name), True) return diff --git a/rare/utils/models.py b/rare/utils/models.py index 5cbf8af3..6bd5b15b 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -5,7 +5,7 @@ class InstallOptions: def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), max_workers: int = os.cpu_count() * 2, repair: bool = False, download_only: bool = False, ignore_free_space: bool = False, force: bool = False, - sdl_list: list = list() + sdl_list: list = [''] ): self.app_name = app_name self.path = path From 148cf0ff8e9850979f39ba84f1052dd29ad4bda0 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sat, 22 May 2021 22:56:13 +0300 Subject: [PATCH 12/22] Add models for the download queue. The addition of download_only option broke the download queue due to different positional arguments and tuple indices. Since using indices is awkward, models to represent the download and each item in the queue itself were added. --- rare/components/dialogs/install_dialog.py | 6 +-- rare/components/tab_widget.py | 4 +- rare/components/tabs/downloads/__init__.py | 44 +++++++++---------- .../tabs/downloads/dl_queue_widget.py | 20 +++++---- .../tabs/downloads/download_thread.py | 18 ++++---- rare/components/tabs/games/game_list.py | 4 +- rare/utils/models.py | 26 ++++++++--- 7 files changed, 68 insertions(+), 54 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 097d4994..2ab0c5a5 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -7,7 +7,7 @@ from custom_legendary.core import LegendaryCore from custom_legendary.utils.selective_dl import games from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.utils.extra_widgets import PathEdit -from rare.utils.models import InstallOptions +from rare.utils.models import InstallOptionsModel from rare.utils.utils import get_size @@ -108,13 +108,13 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.get_install_info(self.app_name, path, self.tags) def on_install_button_clicked(self): - self.options = InstallOptions( + self.options = InstallOptionsModel( app_name=self.app_name, path=self.install_dir_edit.text() if not self.update_game else None, max_workers=self.max_workers_spin.value(), force=self.force_download_check.isChecked(), ignore_free_space=self.ignore_space_check.isChecked(), - download_only=self.download_only_check.isChecked(), + dl_only=self.download_only_check.isChecked(), sdl_list=self.tags ) self.threadpool.clear() diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index 97aea35e..e5931e76 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -14,7 +14,7 @@ from rare.components.tabs.downloads import DownloadTab from rare.components.tabs.games import GameTab from rare.components.tabs.settings import SettingsTab from rare.utils import legendary_utils -from rare.utils.models import InstallOptions +from rare.utils.models import InstallOptionsModel class TabWidget(QTabWidget): @@ -84,7 +84,7 @@ class TabWidget(QTabWidget): self.games_tab.uninstalled_info_widget.info.install_game.connect(self.install_game) # repair game self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.start_download( - InstallOptions(app_name, core.get_installed_game(app_name).install_path, repair=True))) + InstallOptionsModel(app_name, core.get_installed_game(app_name).install_path, repair=True))) # Finished sync self.cloud_saves.finished.connect(self.finished_sync) diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index edfba057..415fc12a 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -2,18 +2,17 @@ import datetime from logging import getLogger from multiprocessing import Queue as MPQueue -from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings -from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \ - QListWidget, QHBoxLayout, QGroupBox +from PyQt5.QtCore import QThread, pyqtSignal, QSettings +from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, \ + QHBoxLayout, QGroupBox from custom_legendary.core import LegendaryCore from custom_legendary.models.downloading import UIUpdate from custom_legendary.models.game import Game, InstalledGame -from custom_legendary.utils.selective_dl import games from rare.components.dialogs.install_dialog import InstallDialog from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget from rare.components.tabs.downloads.download_thread import DownloadThread -from rare.utils.models import InstallOptions +from rare.utils.models import InstallOptionsModel, InstallDownloadModel, InstallQueueItemModel from rare.utils.utils import get_size logger = getLogger("Download") @@ -97,15 +96,15 @@ class DownloadTab(QWidget): def stop_download(self): self.thread.kill() - def install_game(self, options: InstallOptions, from_update=False): + def install_game(self, options: InstallOptionsModel): status_queue = MPQueue() try: - dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( + download = InstallDownloadModel(*self.core.prepare_download( app_name=options.app_name, base_path=options.path, force=options.force, - no_install=options.download_only, + no_install=options.dl_only, status_q=status_queue, # max_shm=, max_workers=options.max_workers, @@ -127,32 +126,31 @@ class DownloadTab(QWidget): # override_delta_manifest=, # reset_sdl=, sdl_prompt=lambda app_name, title: options.sdl_list - ) + )) except Exception as e: QMessageBox.warning(self, self.tr("Error preparing download"), str(e)) return + queue_item = InstallQueueItemModel(status_queue, download, options) + if self.active_game is None: - self.start_installation(dlm, game, status_queue, igame, repair_file, options, analysis, - options.download_only) + self.start_installation(queue_item) else: - self.dl_queue.append( - (dlm, game, status_queue, igame, repair_file, options, analysis, options.download_only)) + self.dl_queue.append(queue_item) self.queue_widget.update_queue(self.dl_queue) - def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions, analysis, - dl_only): + def start_installation(self, queue_item: InstallQueueItemModel): if self.dl_queue: self.dl_queue.pop(0) self.queue_widget.update_queue(self.dl_queue) - self.active_game = game - self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file, dl_only) + self.active_game = queue_item.download.game + self.thread = DownloadThread(self.core, queue_item) self.thread.status.connect(self.status) self.thread.statistics.connect(self.statistics) self.thread.start() self.kill_button.setDisabled(False) - self.analysis = analysis + self.analysis = queue_item.download.analysis self.installing_game.setText(self.tr("Installing Game: ") + self.active_game.app_title) def status(self, text): @@ -177,7 +175,7 @@ class DownloadTab(QWidget): self.active_game = None if self.dl_queue: - if self.dl_queue[0][1] == app_name: + if self.dl_queue[0].download.game.app_name == app_name: self.dl_queue.pop(0) self.queue_widget.update_queue(self.dl_queue) @@ -195,7 +193,7 @@ class DownloadTab(QWidget): self.reset_infos() if len(self.dl_queue) != 0: - self.start_installation(*self.dl_queue[0]) + self.start_installation(self.dl_queue[0]) else: self.queue_widget.update_queue(self.dl_queue) @@ -207,7 +205,7 @@ class DownloadTab(QWidget): self.active_game = None self.finished.emit((False, None)) if self.dl_queue: - self.start_installation(*self.dl_queue[0]) + self.start_installation(self.dl_queue[0]) def reset_infos(self): self.kill_button.setDisabled(True) @@ -237,10 +235,10 @@ class DownloadTab(QWidget): dialog = InstallDialog(app_name, self.core, update=True, parent=self) options = dialog.get_install_options() else: - self.install_game(InstallOptions(app_name=app_name), True) + self.install_game(InstallOptionsModel(app_name=app_name)) return if options: - self.install_game(options, True) + self.install_game(options) else: self.update_widgets[app_name].update_button.setDisabled(False) self.update_widgets[app_name].update_with_settings.setDisabled(False) diff --git a/rare/components/tabs/downloads/dl_queue_widget.py b/rare/components/tabs/downloads/dl_queue_widget.py index 72edd64f..b97a0e8e 100644 --- a/rare/components/tabs/downloads/dl_queue_widget.py +++ b/rare/components/tabs/downloads/dl_queue_widget.py @@ -4,6 +4,8 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QWidget from qtawesome import icon +from rare.utils.models import InstallQueueItemModel + logger = getLogger("QueueWidget") @@ -12,9 +14,9 @@ class DlWidget(QWidget): move_down = pyqtSignal(str) # app_name remove = pyqtSignal(str) # app_name - def __init__(self, index, item): + def __init__(self, index, queue_item: InstallQueueItemModel): super(DlWidget, self).__init__() - self.app_name = item[1].app_name + self.app_name = queue_item.download.game.app_name self.layout = QHBoxLayout() self.left_layout = QVBoxLayout() @@ -33,11 +35,11 @@ class DlWidget(QWidget): self.layout.addLayout(self.left_layout) self.right_layout = QVBoxLayout() - self.title = QLabel(item[1].app_title) + self.title = QLabel(queue_item.download.game.app_title) self.right_layout.addWidget(self.title) - dl_size = item[-1].dl_size - install_size = item[-1].install_size + dl_size = queue_item.download.analysis.dl_size + install_size = queue_item.download.analysis.install_size self.size = QHBoxLayout() @@ -69,7 +71,7 @@ class DlQueueWidget(QGroupBox): self.setLayout(self.layout) def update_queue(self, dl_queue: list): - logger.info("Update Queue " + ", ".join(i[1].app_title for i in dl_queue)) + logger.info("Update Queue " + ", ".join(i.download.game.app_title for i in dl_queue)) self.dl_queue = dl_queue self.setLayout(QVBoxLayout()) QWidget().setLayout(self.layout) @@ -93,7 +95,7 @@ class DlQueueWidget(QGroupBox): def remove(self, app_name): for index, i in enumerate(self.dl_queue): - if i[1].app_name == app_name: + if i.download.game.app_name == app_name: self.dl_queue.pop(index) break else: @@ -106,7 +108,7 @@ class DlQueueWidget(QGroupBox): index: int for i, item in enumerate(self.dl_queue): - if item[1].app_name == app_name: + if item.download.game.app_name == app_name: index = i break else: @@ -120,7 +122,7 @@ class DlQueueWidget(QGroupBox): index: int for i, item in enumerate(self.dl_queue): - if item[1].app_name == app_name: + if item.download.game.app_name == app_name: index = i break else: diff --git a/rare/components/tabs/downloads/download_thread.py b/rare/components/tabs/downloads/download_thread.py index 195ff932..94c4f7c8 100644 --- a/rare/components/tabs/downloads/download_thread.py +++ b/rare/components/tabs/downloads/download_thread.py @@ -4,7 +4,6 @@ import subprocess import sys import time from logging import getLogger -from multiprocessing import Queue as MPQueue from queue import Empty import psutil @@ -12,8 +11,8 @@ from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QMessageBox from custom_legendary.core import LegendaryCore -from custom_legendary.downloader.manager import DLManager from custom_legendary.models.downloading import UIUpdate, WriterTask +from rare.utils.models import InstallQueueItemModel logger = getLogger("Download") @@ -22,16 +21,15 @@ class DownloadThread(QThread): status = pyqtSignal(str) statistics = pyqtSignal(UIUpdate) - def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False, - repair_file=None, dl_only=False): + def __init__(self, core: LegendaryCore, queue_item: InstallQueueItemModel): super(DownloadThread, self).__init__() - self.dlm = dlm + self.dlm = queue_item.download.dlmanager self.core = core - self.dl_only = dl_only - self.status_queue = status_queue - self.igame = igame - self.repair = repair - self.repair_file = repair_file + self.dl_only = queue_item.options.dl_only + self.status_queue = queue_item.queue + self.igame = queue_item.download.igame + self.repair = queue_item.download.repair + self.repair_file = queue_item.download.repair_file self._kill = False def run(self): diff --git a/rare/components/tabs/games/game_list.py b/rare/components/tabs/games/game_list.py index 36b56942..47dc7485 100644 --- a/rare/components/tabs/games/game_list.py +++ b/rare/components/tabs/games/game_list.py @@ -13,14 +13,14 @@ from rare.components.tabs.games.game_widgets.installed_list_widget import Instal from rare.components.tabs.games.game_widgets.uninstalled_icon_widget import IconWidgetUninstalled from rare.components.tabs.games.game_widgets.uninstalled_list_widget import ListWidgetUninstalled from rare.utils.extra_widgets import FlowLayout -from rare.utils.models import InstallOptions +from rare.utils.models import InstallOptionsModel from rare.utils.utils import download_image logger = getLogger("Game list") class GameList(QStackedWidget): - install_game = pyqtSignal(InstallOptions) + install_game = pyqtSignal(InstallOptionsModel) show_game_info = pyqtSignal(str) update_game = pyqtSignal() game_exited = pyqtSignal(str) diff --git a/rare/utils/models.py b/rare/utils/models.py index 6bd5b15b..5dd814d5 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -1,17 +1,33 @@ import os -class InstallOptions: +class InstallOptionsModel: def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), - max_workers: int = os.cpu_count() * 2, repair: bool = False, - download_only: bool = False, ignore_free_space: bool = False, force: bool = False, - sdl_list: list = [''] + max_workers: int = os.cpu_count() * 2, repair: bool = False, dl_only: bool = False, + ignore_free_space: bool = False, force: bool = False, sdl_list: list = [''] ): self.app_name = app_name self.path = path self.max_workers = max_workers self.repair = repair - self.download_only = download_only + self.dl_only = dl_only self.ignore_free_space = ignore_free_space self.force = force self.sdl_list = sdl_list + + +class InstallDownloadModel: + def __init__(self, dlmanager, analysis, game, igame, repair: bool = False, repair_file: str = None): + self.dlmanager = dlmanager + self.analysis = analysis + self.game = game + self.igame = igame + self.repair = repair + self.repair_file = repair_file + + +class InstallQueueItemModel: + def __init__(self, queue, download: InstallDownloadModel, options: InstallOptionsModel): + self.queue = queue + self.download = download + self.options = options From 376d3e0eba4687fd9f7e62033c7c9e346ce6cd8d Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 00:07:47 +0300 Subject: [PATCH 13/22] Don't use global threadpool as it gets deleted when exiting the dialog Woops... --- rare/components/dialogs/install_dialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 2ab0c5a5..db3f715c 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -24,7 +24,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.game = self.core.get_game(app_name) self.update_game = update - self.threadpool = QThreadPool.globalInstance() + self.threadpool = QThreadPool(self) + self.threadpool.setMaxThreadCount(1) header = self.tr("Update") if update else self.tr("Install") self.install_dialog_label.setText(f"

{header} \"{self.game.app_title}\"

") From b0ec5c5fcbf8b7b35ac33bad728e4c310e64e67a Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 15:15:36 +0300 Subject: [PATCH 14/22] Move all download preparations inside InstallDialog. InstallDialog now returns a InstallQueueItemModel ready to be downloaded or queued. Renamed a few model attributes to match legendary's names. InstallDialog can be run silently for auto-downloads. --- rare/components/dialogs/install_dialog.py | 123 +++++++++++------- rare/components/tab_widget.py | 11 +- rare/components/tabs/downloads/__init__.py | 55 +------- .../tabs/downloads/download_thread.py | 6 +- rare/utils/models.py | 13 +- 5 files changed, 100 insertions(+), 108 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index db3f715c..8033b610 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -1,4 +1,5 @@ import os +from multiprocessing import Queue as MPQueue from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox @@ -7,21 +8,22 @@ from custom_legendary.core import LegendaryCore from custom_legendary.utils.selective_dl import games from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.utils.extra_widgets import PathEdit -from rare.utils.models import InstallOptionsModel +from rare.utils.models import InstallDownloadModel, InstallQueueItemModel from rare.utils.utils import get_size class InstallDialog(QDialog, Ui_InstallDialog): - options = False - def __init__(self, app_name, core: LegendaryCore, update=False, parent=None): + def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, parent=None): super(InstallDialog, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setupUi(self) self.core = core - self.app_name = app_name - self.game = self.core.get_game(app_name) + self.dl_item = dl_item + self.dl_item.status_q = MPQueue() + self.app_name = self.dl_item.options.app_name + self.game = self.core.get_game(self.app_name) self.update_game = update self.threadpool = QThreadPool(self) @@ -54,12 +56,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.max_workers_spin.setValue(int(max_workers)) self.sdl_list_checks = list() - self.tags = [''] try: - for key, info in games[app_name].items(): + for key, info in games[self.app_name].items(): cb = QDataCheckBox(info['name'], info['tags']) if key == '__required': - self.tags.extend(info['tags']) + self.dl_item.options.sdl_list.extend(info['tags']) cb.setChecked(True) cb.setDisabled(True) self.sdl_list_layout.addWidget(cb) @@ -71,7 +72,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.sdl_list_frame.setVisible(False) self.sdl_list_label.setVisible(False) - self.get_install_info(app_name, default_path, self.tags) + self.get_options() + self.get_download_info() self.install_button.clicked.connect(self.on_install_button_clicked) self.cancel_button.clicked.connect(self.on_cancel_button_clicked) @@ -79,13 +81,27 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.resize(self.minimumSize()) self.setFixedSize(self.size()) - def get_install_options(self, path=None): + def get_options(self): + self.dl_item.options.path = self.install_dir_edit.text() if not self.update_game else None + self.dl_item.options.max_workers = self.max_workers_spin.value() + self.dl_item.options.force = self.force_download_check.isChecked() + self.dl_item.options.ignore_space_req = self.ignore_space_check.isChecked() + self.dl_item.options.dl_only = self.download_only_check.isChecked() + self.dl_item.options.sdl_list = [''] + for cb in self.sdl_list_checks: + if data := cb.isChecked(): + self.dl_item.options.sdl_list.extend(data) + + def get_download_item(self, path=None, silent=False): if path: self.install_dir_edit.setText(path) - self.exec_() - return self.options + if silent: + self.threadpool.waitForDone() + else: + self.exec_() + return self.dl_item - def get_install_info(self, app_name, path, tags): + def get_download_info(self): message = self.tr("Updating...") self.download_size_info_label.setText(message) self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") @@ -93,44 +109,36 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_button.setEnabled(False) self.sdl_list_frame.setEnabled(False) - info_worker = InstallInfoWorker(app_name, path, tags, self.core) + info_worker = InstallInfoWorker(self.core, self.dl_item) info_worker.setAutoDelete(True) info_worker.signals.finished.connect(self.on_worker_finished) self.threadpool.start(info_worker) def on_sdl_checkbox_changed(self): - self.tags = [''] - for cb in self.sdl_list_checks: - if data := cb.isChecked(): - self.tags.extend(data) - self.get_install_info(self.app_name, self.install_dir_edit.text(), self.tags) + self.get_options() + self.get_download_info() - def on_install_dir_text_changed(self, path: str): - self.get_install_info(self.app_name, path, self.tags) + def on_install_dir_text_changed(self): + self.get_options() + self.get_download_info() def on_install_button_clicked(self): - self.options = InstallOptionsModel( - app_name=self.app_name, - path=self.install_dir_edit.text() if not self.update_game else None, - max_workers=self.max_workers_spin.value(), - force=self.force_download_check.isChecked(), - ignore_free_space=self.ignore_space_check.isChecked(), - dl_only=self.download_only_check.isChecked(), - sdl_list=self.tags - ) self.threadpool.clear() self.close() def on_cancel_button_clicked(self): + self.dl_item = False self.threadpool.clear() self.close() - def on_worker_finished(self, info: tuple): - download_size, install_size = info + def on_worker_finished(self, dl_item: InstallQueueItemModel): # TODO: Check available size and act accordingly # TODO: (show message in label | color it | disable install unless ignore) # TODO: Find a way to get the installation size delta and show it - if download_size is not None and install_size is not None: + if dl_item: + self.dl_item = dl_item + download_size = self.dl_item.download.analysis.dl_size + install_size = self.dl_item.download.analysis.install_size if download_size: self.download_size_info_label.setText("{}".format(get_size(download_size))) self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") @@ -147,37 +155,58 @@ class InstallDialog(QDialog, Ui_InstallDialog): class InstallInfoWorkerSignals(QObject): - finished = pyqtSignal(tuple) + finished = pyqtSignal(InstallQueueItemModel) class InstallInfoWorker(QRunnable): - def __init__(self, app_name, path, tags, core: LegendaryCore): + def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel): super(InstallInfoWorker, self).__init__() self.signals = InstallInfoWorkerSignals() self.core = core - self.app_name = app_name - self.path = path - self.tags = tags + self.dl_item = dl_item @pyqtSlot() def run(self): try: - dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( - app_name=self.app_name, - base_path=self.path, - sdl_prompt=lambda app_name, title: self.tags - ) - self.signals.finished.emit((analysis.dl_size, analysis.install_size)) + download = InstallDownloadModel(*self.core.prepare_download( + app_name=self.dl_item.options.app_name, + base_path=self.dl_item.options.path, + force=self.dl_item.options.force, + no_install=self.dl_item.options.dl_only, + status_q=self.dl_item.status_q, + # max_shm=, + max_workers=self.dl_item.options.max_workers, + # game_folder=, + # disable_patching=, + # override_manifest=, + # override_old_manifest=, + # override_base_url=, + # platform_override=, + # file_prefix_filter=, + # file_exclude_filter=, + # file_install_tag=, + # dl_optimizations=, + # dl_timeout=, + repair=self.dl_item.options.repair, + # repair_use_latest=, + ignore_space_req=self.dl_item.options.ignore_space_req, + # disable_delta=, + # override_delta_manifest=, + # reset_sdl=, + sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list + )) + self.dl_item.download = download except: - self.signals.finished.emit((None, None)) + self.dl_item.download = None + self.signals.finished.emit(self.dl_item) return class QDataCheckBox(QCheckBox): - def __init__(self, text, data=None): - super(QDataCheckBox, self).__init__() + def __init__(self, text, data=None, parent=None): + super(QDataCheckBox, self).__init__(parent) self.setText(text) self.data = data diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index e5931e76..3f4851c4 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -14,7 +14,7 @@ from rare.components.tabs.downloads import DownloadTab from rare.components.tabs.games import GameTab from rare.components.tabs.settings import SettingsTab from rare.utils import legendary_utils -from rare.utils.models import InstallOptionsModel +from rare.utils.models import InstallQueueItemModel, InstallOptionsModel class TabWidget(QTabWidget): @@ -96,11 +96,12 @@ class TabWidget(QTabWidget): self.setIconSize(QSize(25, 25)) def install_game(self, app_name, disable_path=False): - dialog = InstallDialog(app_name, self.core, disable_path, parent=self) - options = dialog.get_install_options() - if options: + download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)) + dialog = InstallDialog(self.core, download_item, update=disable_path, parent=self) + download_item = dialog.get_download_item() + if download_item: self.setCurrentIndex(1) - self.start_download(options) + self.start_download(download_item) def start_download(self, options): downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1 diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 415fc12a..266d0cf2 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -1,6 +1,5 @@ import datetime from logging import getLogger -from multiprocessing import Queue as MPQueue from PyQt5.QtCore import QThread, pyqtSignal, QSettings from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, \ @@ -12,7 +11,7 @@ from custom_legendary.models.game import Game, InstalledGame from rare.components.dialogs.install_dialog import InstallDialog from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget from rare.components.tabs.downloads.download_thread import DownloadThread -from rare.utils.models import InstallOptionsModel, InstallDownloadModel, InstallQueueItemModel +from rare.utils.models import InstallOptionsModel, InstallQueueItemModel from rare.utils.utils import get_size logger = getLogger("Download") @@ -96,44 +95,7 @@ class DownloadTab(QWidget): def stop_download(self): self.thread.kill() - def install_game(self, options: InstallOptionsModel): - - status_queue = MPQueue() - try: - download = InstallDownloadModel(*self.core.prepare_download( - app_name=options.app_name, - base_path=options.path, - force=options.force, - no_install=options.dl_only, - status_q=status_queue, - # max_shm=, - max_workers=options.max_workers, - # game_folder=, - # disable_patching=, - # override_manifest=, - # override_old_manifest=, - # override_base_url=, - # platform_override=, - # file_prefix_filter=, - # file_exclude_filter=, - # file_install_tag=, - # dl_optimizations=, - # dl_timeout=, - repair=options.repair, - # repair_use_latest=, - ignore_space_req=options.ignore_free_space, - # disable_delta=, - # override_delta_manifest=, - # reset_sdl=, - sdl_prompt=lambda app_name, title: options.sdl_list - )) - except Exception as e: - QMessageBox.warning(self, self.tr("Error preparing download"), - str(e)) - return - - queue_item = InstallQueueItemModel(status_queue, download, options) - + def install_game(self, queue_item: InstallQueueItemModel): if self.active_game is None: self.start_installation(queue_item) else: @@ -231,14 +193,11 @@ class DownloadTab(QWidget): def update_game(self, app_name: str, auto=False): logger.info("Update " + app_name) - if not auto: - dialog = InstallDialog(app_name, self.core, update=True, parent=self) - options = dialog.get_install_options() - else: - self.install_game(InstallOptionsModel(app_name=app_name)) - return - if options: - self.install_game(options) + download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)) + dialog = InstallDialog(self.core, download_item, update=True, parent=self) + download_item = dialog.get_download_item(silent=auto) + if download_item: + self.install_game(download_item) else: self.update_widgets[app_name].update_button.setDisabled(False) self.update_widgets[app_name].update_with_settings.setDisabled(False) diff --git a/rare/components/tabs/downloads/download_thread.py b/rare/components/tabs/downloads/download_thread.py index 94c4f7c8..e3b6b5aa 100644 --- a/rare/components/tabs/downloads/download_thread.py +++ b/rare/components/tabs/downloads/download_thread.py @@ -23,10 +23,10 @@ class DownloadThread(QThread): def __init__(self, core: LegendaryCore, queue_item: InstallQueueItemModel): super(DownloadThread, self).__init__() - self.dlm = queue_item.download.dlmanager self.core = core + self.dlm = queue_item.download.dlmanager self.dl_only = queue_item.options.dl_only - self.status_queue = queue_item.queue + self.status_q = queue_item.status_q self.igame = queue_item.download.igame self.repair = queue_item.download.repair self.repair_file = queue_item.download.repair_file @@ -103,7 +103,7 @@ class DownloadThread(QThread): dl_stopped = True try: if not dl_stopped: - self.statistics.emit(self.status_queue.get(timeout=1)) + self.statistics.emit(self.status_q.get(timeout=1)) except queue.Empty: pass diff --git a/rare/utils/models.py b/rare/utils/models.py index 5dd814d5..7d752f7f 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -4,20 +4,20 @@ import os class InstallOptionsModel: def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), max_workers: int = os.cpu_count() * 2, repair: bool = False, dl_only: bool = False, - ignore_free_space: bool = False, force: bool = False, sdl_list: list = [''] + ignore_space_req: bool = False, force: bool = False, sdl_list: list = [''] ): self.app_name = app_name self.path = path self.max_workers = max_workers self.repair = repair self.dl_only = dl_only - self.ignore_free_space = ignore_free_space + self.ignore_space_req = ignore_space_req self.force = force self.sdl_list = sdl_list class InstallDownloadModel: - def __init__(self, dlmanager, analysis, game, igame, repair: bool = False, repair_file: str = None): + def __init__(self, dlmanager, analysis, game, igame, repair: bool, repair_file: str): self.dlmanager = dlmanager self.analysis = analysis self.game = game @@ -27,7 +27,10 @@ class InstallDownloadModel: class InstallQueueItemModel: - def __init__(self, queue, download: InstallDownloadModel, options: InstallOptionsModel): - self.queue = queue + def __init__(self, status_q=None, download: InstallDownloadModel = None, options: InstallOptionsModel = None): + self.status_q = status_q self.download = download self.options = options + + def __bool__(self): + return (self.status_q is not None) and (self.download is not None) and (self.options is not None) From 181636f2befb7153d1b0387e2f530f06e216bb36 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 15:52:39 +0300 Subject: [PATCH 15/22] Connect all widgets to the get_download_info function. --- rare/components/dialogs/install_dialog.py | 26 +++++++++---------- .../tabs/downloads/download_thread.py | 4 +-- rare/utils/models.py | 8 +++--- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 8033b610..fff1b949 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -42,7 +42,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_dir_edit = PathEdit(text=default_path, file_type=QFileDialog.DirectoryOnly, - edit_func=self.on_install_dir_text_changed) + edit_func=self.get_download_info) self.install_dir_layout.addWidget(self.install_dir_edit) if update: @@ -54,6 +54,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): else: max_workers = 0 self.max_workers_spin.setValue(int(max_workers)) + self.max_workers_spin.valueChanged.connect(self.get_download_info) + + self.force_download_check.stateChanged.connect(self.get_download_info) + self.ignore_space_check.stateChanged.connect(self.get_download_info) + self.download_only_check.stateChanged.connect(self.get_download_info) self.sdl_list_checks = list() try: @@ -67,7 +72,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.sdl_list_checks.append(cb) self.sdl_list_frame.resize(self.sdl_list_frame.minimumSize()) for cb in self.sdl_list_checks: - cb.stateChanged.connect(self.on_sdl_checkbox_changed) + cb.stateChanged.connect(self.get_download_info) except KeyError: self.sdl_list_frame.setVisible(False) self.sdl_list_label.setVisible(False) @@ -82,11 +87,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.setFixedSize(self.size()) def get_options(self): - self.dl_item.options.path = self.install_dir_edit.text() if not self.update_game else None + self.dl_item.options.base_path = self.install_dir_edit.text() if not self.update_game else None self.dl_item.options.max_workers = self.max_workers_spin.value() self.dl_item.options.force = self.force_download_check.isChecked() self.dl_item.options.ignore_space_req = self.ignore_space_check.isChecked() - self.dl_item.options.dl_only = self.download_only_check.isChecked() + self.dl_item.options.no_install = self.download_only_check.isChecked() self.dl_item.options.sdl_list = [''] for cb in self.sdl_list_checks: if data := cb.isChecked(): @@ -109,19 +114,12 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_button.setEnabled(False) self.sdl_list_frame.setEnabled(False) + self.get_options() info_worker = InstallInfoWorker(self.core, self.dl_item) info_worker.setAutoDelete(True) info_worker.signals.finished.connect(self.on_worker_finished) self.threadpool.start(info_worker) - def on_sdl_checkbox_changed(self): - self.get_options() - self.get_download_info() - - def on_install_dir_text_changed(self): - self.get_options() - self.get_download_info() - def on_install_button_clicked(self): self.threadpool.clear() self.close() @@ -171,9 +169,9 @@ class InstallInfoWorker(QRunnable): try: download = InstallDownloadModel(*self.core.prepare_download( app_name=self.dl_item.options.app_name, - base_path=self.dl_item.options.path, + base_path=self.dl_item.options.base_path, force=self.dl_item.options.force, - no_install=self.dl_item.options.dl_only, + no_install=self.dl_item.options.no_install, status_q=self.dl_item.status_q, # max_shm=, max_workers=self.dl_item.options.max_workers, diff --git a/rare/components/tabs/downloads/download_thread.py b/rare/components/tabs/downloads/download_thread.py index e3b6b5aa..923c06ac 100644 --- a/rare/components/tabs/downloads/download_thread.py +++ b/rare/components/tabs/downloads/download_thread.py @@ -25,7 +25,7 @@ class DownloadThread(QThread): super(DownloadThread, self).__init__() self.core = core self.dlm = queue_item.download.dlmanager - self.dl_only = queue_item.options.dl_only + self.no_install = queue_item.options.no_install self.status_q = queue_item.status_q self.igame = queue_item.download.igame self.repair = queue_item.download.repair @@ -122,7 +122,7 @@ class DownloadThread(QThread): logger.info(f"Download finished in {start_time - end_t}s") game = self.core.get_game(self.igame.app_name) - if not self.dl_only: + if not self.no_install: postinstall = self.core.install_game(self.igame) if postinstall: self._handle_postinstall(postinstall, self.igame) diff --git a/rare/utils/models.py b/rare/utils/models.py index 7d752f7f..44400ddc 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -2,15 +2,15 @@ import os class InstallOptionsModel: - def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), - max_workers: int = os.cpu_count() * 2, repair: bool = False, dl_only: bool = False, + def __init__(self, app_name: str, base_path: str = os.path.expanduser("~/legendary"), + max_workers: int = os.cpu_count() * 2, repair: bool = False, no_install: bool = False, ignore_space_req: bool = False, force: bool = False, sdl_list: list = [''] ): self.app_name = app_name - self.path = path + self.base_path = base_path self.max_workers = max_workers self.repair = repair - self.dl_only = dl_only + self.no_install = no_install self.ignore_space_req = ignore_space_req self.force = force self.sdl_list = sdl_list From fd84065fcb0ae28ff301ff1d21fd27b5beab47d8 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 19:38:33 +0300 Subject: [PATCH 16/22] Add verify button to not block on every change. Add some behavioral safeguards until thread can be stopped. --- rare/components/dialogs/install_dialog.py | 47 ++++++++++++++------ rare/ui/components/dialogs/install_dialog.py | 4 ++ rare/ui/components/dialogs/install_dialog.ui | 7 +++ 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index fff1b949..b14b8e25 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -2,6 +2,7 @@ import os from multiprocessing import Queue as MPQueue from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot +from PyQt5.QtGui import QCloseEvent from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox from custom_legendary.core import LegendaryCore @@ -17,6 +18,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, parent=None): super(InstallDialog, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose, True) + self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowStaysOnTopHint) self.setupUi(self) self.core = core @@ -42,7 +44,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_dir_edit = PathEdit(text=default_path, file_type=QFileDialog.DirectoryOnly, - edit_func=self.get_download_info) + edit_func=self.on_option_widget_changed) self.install_dir_layout.addWidget(self.install_dir_edit) if update: @@ -54,11 +56,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): else: max_workers = 0 self.max_workers_spin.setValue(int(max_workers)) - self.max_workers_spin.valueChanged.connect(self.get_download_info) + self.max_workers_spin.valueChanged.connect(self.on_option_widget_changed) - self.force_download_check.stateChanged.connect(self.get_download_info) - self.ignore_space_check.stateChanged.connect(self.get_download_info) - self.download_only_check.stateChanged.connect(self.get_download_info) + self.force_download_check.stateChanged.connect(self.on_option_widget_changed) + self.ignore_space_check.stateChanged.connect(self.on_option_widget_changed) + self.download_only_check.stateChanged.connect(self.on_option_widget_changed) self.sdl_list_checks = list() try: @@ -72,7 +74,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.sdl_list_checks.append(cb) self.sdl_list_frame.resize(self.sdl_list_frame.minimumSize()) for cb in self.sdl_list_checks: - cb.stateChanged.connect(self.get_download_info) + cb.stateChanged.connect(self.on_option_widget_changed) except KeyError: self.sdl_list_frame.setVisible(False) self.sdl_list_label.setVisible(False) @@ -80,8 +82,12 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.get_options() self.get_download_info() - self.install_button.clicked.connect(self.on_install_button_clicked) self.cancel_button.clicked.connect(self.on_cancel_button_clicked) + self.verify_button.clicked.connect(self.get_download_info) + self.install_button.clicked.connect(self.on_install_button_clicked) + + self.options_changed = False + self.worker_running = False self.resize(self.minimumSize()) self.setFixedSize(self.size()) @@ -112,20 +118,28 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_size_info_label.setText(message) self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") + self.cancel_button.setEnabled(False) + self.verify_button.setEnabled(False) self.install_button.setEnabled(False) - self.sdl_list_frame.setEnabled(False) - self.get_options() + self.options_changed = False + self.worker_running = True info_worker = InstallInfoWorker(self.core, self.dl_item) info_worker.setAutoDelete(True) info_worker.signals.finished.connect(self.on_worker_finished) self.threadpool.start(info_worker) - def on_install_button_clicked(self): + def on_option_widget_changed(self): + self.options_changed = True + self.install_button.setEnabled(False) + self.verify_button.setEnabled(not self.worker_running) + self.get_options() + + def on_cancel_button_clicked(self): + self.dl_item.download = None self.threadpool.clear() self.close() - def on_cancel_button_clicked(self): - self.dl_item = False + def on_install_button_clicked(self): self.threadpool.clear() self.close() @@ -133,6 +147,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): # TODO: Check available size and act accordingly # TODO: (show message in label | color it | disable install unless ignore) # TODO: Find a way to get the installation size delta and show it + self.worker_running = False if dl_item: self.dl_item = dl_item download_size = self.dl_item.download.analysis.dl_size @@ -140,7 +155,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): if download_size: self.download_size_info_label.setText("{}".format(get_size(download_size))) self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") - self.install_button.setEnabled(True) + self.install_button.setEnabled(not self.options_changed) else: self.install_size_info_label.setText(self.tr("Game already installed")) self.install_size_info_label.setStyleSheet("font-style: italics; font-weight: normal") @@ -149,8 +164,14 @@ class InstallDialog(QDialog, Ui_InstallDialog): else: self.download_size_info_label.setText("Error") self.install_size_info_label.setText("Error") + self.verify_button.setEnabled(self.options_changed) + self.cancel_button.setEnabled(True) self.sdl_list_frame.setEnabled(True) + def closeEvent(self, a0: QCloseEvent) -> None: + self.threadpool.waitForDone() + self.on_cancel_button_clicked() + class InstallInfoWorkerSignals(QObject): finished = pyqtSignal(InstallQueueItemModel) diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index 467d4f4c..533703f8 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -27,6 +27,9 @@ class Ui_InstallDialog(object): self.cancel_button = QtWidgets.QPushButton(InstallDialog) self.cancel_button.setObjectName("cancel_button") self.button_layout.addWidget(self.cancel_button) + self.verify_button = QtWidgets.QPushButton(InstallDialog) + self.verify_button.setObjectName("verify_button") + self.button_layout.addWidget(self.verify_button) self.install_button = QtWidgets.QPushButton(InstallDialog) self.install_button.setObjectName("install_button") self.button_layout.addWidget(self.install_button) @@ -115,6 +118,7 @@ class Ui_InstallDialog(object): _translate = QtCore.QCoreApplication.translate self.force_download_label.setText(_translate("InstallDialog", "Force download")) self.cancel_button.setText(_translate("InstallDialog", "Cancel")) + self.verify_button.setText(_translate("InstallDialog", "Verify")) self.install_button.setText(_translate("InstallDialog", "Install")) self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!")) self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index b8a26ba1..0484cd82 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -35,6 +35,13 @@
+ + + + Verify + + + From e019c2ab7e04da0f6e4928e067e21a45fd7e7a25 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 21:00:21 +0300 Subject: [PATCH 17/22] Don't disable cancel button, wait for thread instead. --- rare/components/dialogs/install_dialog.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index b14b8e25..249f82f7 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -118,7 +118,6 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_size_info_label.setText(message) self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") - self.cancel_button.setEnabled(False) self.verify_button.setEnabled(False) self.install_button.setEnabled(False) self.options_changed = False @@ -135,6 +134,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.get_options() def on_cancel_button_clicked(self): + self.threadpool.waitForDone() self.dl_item.download = None self.threadpool.clear() self.close() @@ -165,11 +165,9 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.download_size_info_label.setText("Error") self.install_size_info_label.setText("Error") self.verify_button.setEnabled(self.options_changed) - self.cancel_button.setEnabled(True) self.sdl_list_frame.setEnabled(True) def closeEvent(self, a0: QCloseEvent) -> None: - self.threadpool.waitForDone() self.on_cancel_button_clicked() From c1d5c9894589227fc2b6515e0083f669fb853583 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Sun, 23 May 2021 22:50:31 +0300 Subject: [PATCH 18/22] Disable cancel button when thread is running. --- rare/components/dialogs/install_dialog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 249f82f7..785842c8 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -18,7 +18,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, parent=None): super(InstallDialog, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose, True) - self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowStaysOnTopHint) + self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) self.setupUi(self) self.core = core @@ -118,6 +118,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") self.install_size_info_label.setText(message) self.install_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") + self.cancel_button.setEnabled(False) self.verify_button.setEnabled(False) self.install_button.setEnabled(False) self.options_changed = False @@ -165,6 +166,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.download_size_info_label.setText("Error") self.install_size_info_label.setText("Error") self.verify_button.setEnabled(self.options_changed) + self.cancel_button.setEnabled(True) self.sdl_list_frame.setEnabled(True) def closeEvent(self, a0: QCloseEvent) -> None: From 0200347e72a2f6d6a41b11dc1eb3efa0fcd0bcfb Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Tue, 25 May 2021 23:37:50 +0300 Subject: [PATCH 19/22] Convert InstallDialog to modeless so it doesn't block the main UI any more. --- rare/components/dialogs/install_dialog.py | 147 +++++++++++-------- rare/components/tab_widget.py | 9 +- rare/components/tabs/downloads/__init__.py | 13 +- rare/ui/components/dialogs/install_dialog.py | 12 +- rare/ui/components/dialogs/install_dialog.ui | 16 +- rare/ui/components/dialogs/launch_dialog.py | 5 +- rare/ui/components/dialogs/launch_dialog.ui | 104 ++++++------- 7 files changed, 174 insertions(+), 132 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 785842c8..d5cbb4dc 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -3,7 +3,7 @@ from multiprocessing import Queue as MPQueue from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot from PyQt5.QtGui import QCloseEvent -from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox +from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox, QMessageBox from custom_legendary.core import LegendaryCore from custom_legendary.utils.selective_dl import games @@ -14,19 +14,21 @@ from rare.utils.utils import get_size class InstallDialog(QDialog, Ui_InstallDialog): + result_ready = pyqtSignal(InstallQueueItemModel) - def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, parent=None): + def __init__(self, core: LegendaryCore, dl_item: InstallQueueItemModel, update=False, silent=False, parent=None): super(InstallDialog, self).__init__(parent) + self.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) - self.setupUi(self) self.core = core self.dl_item = dl_item self.dl_item.status_q = MPQueue() self.app_name = self.dl_item.options.app_name self.game = self.core.get_game(self.app_name) - self.update_game = update + self.update = update + self.silent = silent self.threadpool = QThreadPool(self) self.threadpool.setMaxThreadCount(1) @@ -44,10 +46,10 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_dir_edit = PathEdit(text=default_path, file_type=QFileDialog.DirectoryOnly, - edit_func=self.on_option_widget_changed) + edit_func=self.option_changed) self.install_dir_layout.addWidget(self.install_dir_edit) - if update: + if self.update: self.install_dir_label.setVisible(False) self.install_dir_edit.setVisible(False) @@ -56,11 +58,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): else: max_workers = 0 self.max_workers_spin.setValue(int(max_workers)) - self.max_workers_spin.valueChanged.connect(self.on_option_widget_changed) + self.max_workers_spin.valueChanged.connect(self.option_changed) - self.force_download_check.stateChanged.connect(self.on_option_widget_changed) - self.ignore_space_check.stateChanged.connect(self.on_option_widget_changed) - self.download_only_check.stateChanged.connect(self.on_option_widget_changed) + self.force_download_check.stateChanged.connect(self.option_changed) + self.ignore_space_check.stateChanged.connect(self.option_changed) + self.download_only_check.stateChanged.connect(self.option_changed) self.sdl_list_checks = list() try: @@ -74,26 +76,32 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.sdl_list_checks.append(cb) self.sdl_list_frame.resize(self.sdl_list_frame.minimumSize()) for cb in self.sdl_list_checks: - cb.stateChanged.connect(self.on_option_widget_changed) + cb.stateChanged.connect(self.option_changed) except KeyError: self.sdl_list_frame.setVisible(False) self.sdl_list_label.setVisible(False) - self.get_options() - self.get_download_info() + self.install_button.setEnabled(False) - self.cancel_button.clicked.connect(self.on_cancel_button_clicked) - self.verify_button.clicked.connect(self.get_download_info) - self.install_button.clicked.connect(self.on_install_button_clicked) + self.cancel_button.clicked.connect(self.cancel_clicked) + self.verify_button.clicked.connect(self.verify_clicked) + self.install_button.clicked.connect(self.install_clicked) self.options_changed = False self.worker_running = False + self.reject_close = True self.resize(self.minimumSize()) self.setFixedSize(self.size()) + if self.silent: + self.reject_close = False + self.get_download_info() + else: + self.show() + def get_options(self): - self.dl_item.options.base_path = self.install_dir_edit.text() if not self.update_game else None + self.dl_item.options.base_path = self.install_dir_edit.text() if not self.update else None self.dl_item.options.max_workers = self.max_workers_spin.value() self.dl_item.options.force = self.force_download_check.isChecked() self.dl_item.options.ignore_space_req = self.ignore_space_check.isChecked() @@ -103,16 +111,17 @@ class InstallDialog(QDialog, Ui_InstallDialog): if data := cb.isChecked(): self.dl_item.options.sdl_list.extend(data) - def get_download_item(self, path=None, silent=False): - if path: - self.install_dir_edit.setText(path) - if silent: - self.threadpool.waitForDone() - else: - self.exec_() - return self.dl_item - def get_download_info(self): + self.dl_item.download = None + info_worker = InstallInfoWorker(self.core, self.dl_item) + info_worker.setAutoDelete(True) + info_worker.signals.result.connect(self.on_worker_result) + info_worker.signals.failed.connect(self.on_worker_failed) + info_worker.signals.finished.connect(self.on_worker_finished) + self.worker_running = True + self.threadpool.start(info_worker) + + def verify_clicked(self): message = self.tr("Updating...") self.download_size_info_label.setText(message) self.download_size_info_label.setStyleSheet("font-style: italic; font-weight: normal") @@ -122,59 +131,72 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.verify_button.setEnabled(False) self.install_button.setEnabled(False) self.options_changed = False - self.worker_running = True - info_worker = InstallInfoWorker(self.core, self.dl_item) - info_worker.setAutoDelete(True) - info_worker.signals.finished.connect(self.on_worker_finished) - self.threadpool.start(info_worker) + self.get_options() + self.get_download_info() - def on_option_widget_changed(self): + def option_changed(self): self.options_changed = True self.install_button.setEnabled(False) self.verify_button.setEnabled(not self.worker_running) - self.get_options() - def on_cancel_button_clicked(self): - self.threadpool.waitForDone() + def cancel_clicked(self): self.dl_item.download = None - self.threadpool.clear() + self.reject_close = False self.close() - def on_install_button_clicked(self): - self.threadpool.clear() + def install_clicked(self): + self.reject_close = False self.close() - def on_worker_finished(self, dl_item: InstallQueueItemModel): + def on_worker_result(self, dl_item: InstallDownloadModel): + self.dl_item.download = dl_item # TODO: Check available size and act accordingly # TODO: (show message in label | color it | disable install unless ignore) # TODO: Find a way to get the installation size delta and show it - self.worker_running = False - if dl_item: - self.dl_item = dl_item - download_size = self.dl_item.download.analysis.dl_size - install_size = self.dl_item.download.analysis.install_size - if download_size: - self.download_size_info_label.setText("{}".format(get_size(download_size))) - self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") - self.install_button.setEnabled(not self.options_changed) - else: - self.install_size_info_label.setText(self.tr("Game already installed")) - self.install_size_info_label.setStyleSheet("font-style: italics; font-weight: normal") - self.install_size_info_label.setText("{}".format(get_size(install_size))) - self.install_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") + download_size = self.dl_item.download.analysis.dl_size + install_size = self.dl_item.download.analysis.install_size + if download_size: + self.download_size_info_label.setText("{}".format(get_size(download_size))) + self.download_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") + self.install_button.setEnabled(not self.options_changed) else: - self.download_size_info_label.setText("Error") - self.install_size_info_label.setText("Error") + self.install_size_info_label.setText(self.tr("Game already installed")) + self.install_size_info_label.setStyleSheet("font-style: italics; font-weight: normal") + self.install_size_info_label.setText("{}".format(get_size(install_size))) + self.install_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") self.verify_button.setEnabled(self.options_changed) self.cancel_button.setEnabled(True) - self.sdl_list_frame.setEnabled(True) + def on_worker_failed(self, message: str): + error_text = self.tr("Error") + self.download_size_info_label.setText(error_text) + self.install_size_info_label.setText(error_text) + QMessageBox.critical(self, self.windowTitle(), message) + self.verify_button.setEnabled(self.options_changed) + self.cancel_button.setEnabled(True) + + def on_worker_finished(self): + self.worker_running = False + if self.silent: + self.close() + + # lk: happens when close() is called, also when top right 'X' is pressed. + # lk: reject any events not coming from the buttons in case the wm + # lk: doesn't honor the window hints def closeEvent(self, a0: QCloseEvent) -> None: - self.on_cancel_button_clicked() + if self.reject_close: + a0.ignore() + else: + self.threadpool.clear() + self.threadpool.waitForDone() + self.result_ready.emit(self.dl_item) + a0.accept() class InstallInfoWorkerSignals(QObject): - finished = pyqtSignal(InstallQueueItemModel) + result = pyqtSignal(InstallDownloadModel) + failed = pyqtSignal(str) + finished = pyqtSignal() class InstallInfoWorker(QRunnable): @@ -215,11 +237,10 @@ class InstallInfoWorker(QRunnable): # reset_sdl=, sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list )) - self.dl_item.download = download - except: - self.dl_item.download = None - self.signals.finished.emit(self.dl_item) - return + self.signals.result.emit(download) + except RuntimeError as e: + self.signals.failed.emit(str(e)) + self.signals.finished.emit() class QDataCheckBox(QCheckBox): diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index 3f4851c4..9d033e75 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -96,9 +96,12 @@ class TabWidget(QTabWidget): self.setIconSize(QSize(25, 25)) def install_game(self, app_name, disable_path=False): - download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)) - dialog = InstallDialog(self.core, download_item, update=disable_path, parent=self) - download_item = dialog.get_download_item() + install_dialog = InstallDialog(self.core, + InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)), + update=disable_path, parent=self) + install_dialog.result_ready.connect(self.on_install_dialog_closed) + + def on_install_dialog_closed(self, download_item: InstallQueueItemModel): if download_item: self.setCurrentIndex(1) self.start_download(download_item) diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 266d0cf2..6687cb67 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -193,14 +193,17 @@ class DownloadTab(QWidget): def update_game(self, app_name: str, auto=False): logger.info("Update " + app_name) - download_item = InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)) - dialog = InstallDialog(self.core, download_item, update=True, parent=self) - download_item = dialog.get_download_item(silent=auto) + install_dialog = InstallDialog(self.core, + InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)), + update=True, silent=auto, parent=self) + install_dialog.result_ready.connect(self.on_install_dialog_closed) + + def on_install_dialog_closed(self, download_item: InstallQueueItemModel): if download_item: self.install_game(download_item) else: - self.update_widgets[app_name].update_button.setDisabled(False) - self.update_widgets[app_name].update_with_settings.setDisabled(False) + self.update_widgets[download_item.options.app_name].update_button.setDisabled(False) + self.update_widgets[download_item.options.app_name].update_with_settings.setDisabled(False) class UpdateWidget(QWidget): diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index 533703f8..58a817ca 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -79,14 +79,18 @@ class Ui_InstallDialog(object): self.max_workers_label.setObjectName("max_workers_label") self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight) self.install_size_info_label = QtWidgets.QLabel(InstallDialog) - self.install_size_info_label.setText("") + font = QtGui.QFont() + font.setItalic(True) + self.install_size_info_label.setFont(font) self.install_size_info_label.setObjectName("install_size_info_label") self.install_dialog_layout.addWidget(self.install_size_info_label, 8, 1, 1, 2) self.download_size_label = QtWidgets.QLabel(InstallDialog) self.download_size_label.setObjectName("download_size_label") self.install_dialog_layout.addWidget(self.download_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight) self.download_size_info_label = QtWidgets.QLabel(InstallDialog) - self.download_size_info_label.setText("") + font = QtGui.QFont() + font.setItalic(True) + self.download_size_info_label.setFont(font) self.download_size_info_label.setObjectName("download_size_info_label") self.install_dialog_layout.addWidget(self.download_size_info_label, 7, 1, 1, 2) self.force_download_check = QtWidgets.QCheckBox(InstallDialog) @@ -116,7 +120,7 @@ class Ui_InstallDialog(object): def retranslateUi(self, InstallDialog): _translate = QtCore.QCoreApplication.translate - self.force_download_label.setText(_translate("InstallDialog", "Force download")) + self.force_download_label.setText(_translate("InstallDialog", "Force redownload")) self.cancel_button.setText(_translate("InstallDialog", "Cancel")) self.verify_button.setText(_translate("InstallDialog", "Verify")) self.install_button.setText(_translate("InstallDialog", "Install")) @@ -128,7 +132,9 @@ class Ui_InstallDialog(object): self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) self.download_only_label.setText(_translate("InstallDialog", "Download only")) self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) + self.install_size_info_label.setText(_translate("InstallDialog", "Click verify...")) self.download_size_label.setText(_translate("InstallDialog", "Download size")) + self.download_size_info_label.setText(_translate("InstallDialog", "Click verify...")) self.install_dialog_label.setText(_translate("InstallDialog", "error")) self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs")) diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index 0484cd82..d6ee9141 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -9,7 +9,7 @@ - Force download + Force redownload @@ -146,8 +146,13 @@ + + + true + + - + Click verify... @@ -160,8 +165,13 @@
+ + + true + + - + Click verify... diff --git a/rare/ui/components/dialogs/launch_dialog.py b/rare/ui/components/dialogs/launch_dialog.py index a563f459..bc78839f 100644 --- a/rare/ui/components/dialogs/launch_dialog.py +++ b/rare/ui/components/dialogs/launch_dialog.py @@ -8,7 +8,7 @@ # run again. Do not edit this file unless you know what you are doing. -from PyQt5 import QtCore, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets class Ui_LaunchDialog(object): @@ -40,7 +40,7 @@ class Ui_LaunchDialog(object): def retranslateUi(self, LaunchDialog): _translate = QtCore.QCoreApplication.translate - LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Dialog")) + LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching Rare")) self.title_label.setText(_translate("LaunchDialog", "

Launching Rare

")) self.image_info.setText(_translate("LaunchDialog", "Downloading images")) self.steam_info.setText(_translate("LaunchDialog", "Getting Steam grades")) @@ -48,7 +48,6 @@ class Ui_LaunchDialog(object): if __name__ == "__main__": import sys - app = QtWidgets.QApplication(sys.argv) LaunchDialog = QtWidgets.QDialog() ui = Ui_LaunchDialog() diff --git a/rare/ui/components/dialogs/launch_dialog.ui b/rare/ui/components/dialogs/launch_dialog.ui index a5b0a134..e82ac7af 100644 --- a/rare/ui/components/dialogs/launch_dialog.ui +++ b/rare/ui/components/dialogs/launch_dialog.ui @@ -1,56 +1,56 @@ - LaunchDialog - - - - 0 - 0 - 400 - 168 - - - - Dialog - - - - - - <h2>Launching Rare</h2> - - - - - - - 0 - - - - - - - Downloading images - - - - - - - 0 - - - - - - - Getting Steam grades - - - - + LaunchDialog + + + + 0 + 0 + 400 + 168 + + + + Launching Rare + + + + + + <h2>Launching Rare</h2> + - - + + + + + 0 + + + + + + + Downloading images + + + + + + + 0 + + + + + + + Getting Steam grades + + + + + + + From 5b2cc055ff8b90d41be82635bcc1356602d868a5 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Tue, 25 May 2021 23:49:53 +0300 Subject: [PATCH 20/22] Close at the correct signals --- rare/components/dialogs/install_dialog.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index d5cbb4dc..2bedd2f5 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -166,6 +166,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.install_size_info_label.setStyleSheet("font-style: normal; font-weight: bold") self.verify_button.setEnabled(self.options_changed) self.cancel_button.setEnabled(True) + if self.silent: + self.close() def on_worker_failed(self, message: str): error_text = self.tr("Error") @@ -174,14 +176,14 @@ class InstallDialog(QDialog, Ui_InstallDialog): QMessageBox.critical(self, self.windowTitle(), message) self.verify_button.setEnabled(self.options_changed) self.cancel_button.setEnabled(True) - - def on_worker_finished(self): - self.worker_running = False if self.silent: self.close() + def on_worker_finished(self): + self.worker_running = False + # lk: happens when close() is called, also when top right 'X' is pressed. - # lk: reject any events not coming from the buttons in case the wm + # lk: reject any events not coming from the buttons in case the WM # lk: doesn't honor the window hints def closeEvent(self, a0: QCloseEvent) -> None: if self.reject_close: From 9a307856f671d0ab8250bb888da442fc2d556677 Mon Sep 17 00:00:00 2001 From: Stelios Tsampas Date: Wed, 26 May 2021 09:37:55 +0300 Subject: [PATCH 21/22] Ensure the result signal has been connected --- rare/components/dialogs/install_dialog.py | 1 + rare/components/tab_widget.py | 1 + rare/components/tabs/downloads/__init__.py | 1 + 3 files changed, 3 insertions(+) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 2bedd2f5..42572669 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -94,6 +94,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.resize(self.minimumSize()) self.setFixedSize(self.size()) + def execute(self): if self.silent: self.reject_close = False self.get_download_info() diff --git a/rare/components/tab_widget.py b/rare/components/tab_widget.py index 9d033e75..6df0908c 100644 --- a/rare/components/tab_widget.py +++ b/rare/components/tab_widget.py @@ -100,6 +100,7 @@ class TabWidget(QTabWidget): InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)), update=disable_path, parent=self) install_dialog.result_ready.connect(self.on_install_dialog_closed) + install_dialog.execute() def on_install_dialog_closed(self, download_item: InstallQueueItemModel): if download_item: diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 6687cb67..7e85e779 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -197,6 +197,7 @@ class DownloadTab(QWidget): InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)), update=True, silent=auto, parent=self) install_dialog.result_ready.connect(self.on_install_dialog_closed) + install_dialog.execute() def on_install_dialog_closed(self, download_item: InstallQueueItemModel): if download_item: From 77dc6edd711394b2b9998d63853d4dbfd352f0e5 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Thu, 27 May 2021 12:58:34 +0200 Subject: [PATCH 22/22] Fix library reload bug --- rare/components/tabs/games/game_list.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rare/components/tabs/games/game_list.py b/rare/components/tabs/games/game_list.py index 36b56942..f9491f91 100644 --- a/rare/components/tabs/games/game_list.py +++ b/rare/components/tabs/games/game_list.py @@ -346,14 +346,14 @@ class GameList(QStackedWidget): else: installed_names = [i.app_name for i in self.core.get_installed_list()] # get Uninstalled games - uninstalled_games = [] + uninstalled_names = [] games = self.core.get_game_list(True) for game in sorted(games, key=lambda x: x.app_title): if not game.app_name in installed_names: - uninstalled_games.append(game.app_name) + uninstalled_names.append(game.app_name) new_installed_games = list(set(installed_names) - set([i.app_name for i in self.installed])) - new_uninstalled_games = list(set(uninstalled_games) - set([i.app_name for i in self.uninstalled_names])) + new_uninstalled_games = list(set(uninstalled_names) - set([i.app_name for i in self.uninstalled_games])) if (not new_uninstalled_games) and (not new_installed_games): return @@ -388,7 +388,7 @@ class GameList(QStackedWidget): for game in sorted(games, key=lambda x: x.app_title): if not game.app_name in installed_names: self.uninstalled_names.append(game) - for name in uninstalled_games: + for name in uninstalled_names: i_widget, list_widget = self.widgets[name] self.icon_layout.addWidget(i_widget) self.list_layout.addWidget(list_widget)