diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py
index 8d80b689fa..2c2dcabf63 100644
--- a/src/Mod/AddonManager/AddonManager.py
+++ b/src/Mod/AddonManager/AddonManager.py
@@ -53,6 +53,7 @@ import addonmanager_utilities as utils
import AddonManager_rc # This is required by Qt, it's not unused
from package_list import PackageList, PackageListItemModel
from package_details import PackageDetails
+from Widgets.addonmanager_widget_global_buttons import WidgetGlobalButtonBar
from Addon import Addon
from manage_python_dependencies import (
PythonPackageManager,
@@ -130,6 +131,7 @@ class CommandAddonManager:
self.update_all_worker = None
self.developer_mode = None
self.installer_gui = None
+ self.button_bar = None
self.update_cache = False
self.dialog = None
@@ -185,21 +187,21 @@ class CommandAddonManager:
w = pref.GetInt("WindowWidth", 800)
h = pref.GetInt("WindowHeight", 600)
self.dialog.resize(w, h)
+ self.button_bar = WidgetGlobalButtonBar(self.dialog)
# If we are checking for updates automatically, hide the Check for updates button:
autocheck = pref.GetBool("AutoCheck", False)
if autocheck:
- self.dialog.buttonCheckForUpdates.hide()
+ self.button_bar.check_for_updates.hide()
else:
- self.dialog.buttonUpdateAll.hide()
+ self.button_bar.update_all_addons.hide()
# Set up the listing of packages using the model-view-controller architecture
self.packageList = PackageList(self.dialog)
self.item_model = PackageListItemModel()
self.packageList.setModel(self.item_model)
- self.dialog.contentPlaceholder.hide()
- self.dialog.layout().replaceWidget(self.dialog.contentPlaceholder, self.packageList)
- self.packageList.show()
+ self.dialog.layout().addWidget(self.packageList)
+ self.dialog.layout().addWidget(self.button_bar)
# Package details start out hidden
self.packageDetails = PackageDetails(self.dialog)
@@ -209,35 +211,30 @@ class CommandAddonManager:
# set nice icons to everything, by theme with fallback to FreeCAD icons
self.dialog.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
- self.dialog.buttonUpdateAll.setIcon(QtGui.QIcon(":/icons/button_valid.svg"))
- self.dialog.buttonCheckForUpdates.setIcon(QtGui.QIcon(":/icons/view-refresh.svg"))
- self.dialog.buttonClose.setIcon(
- QtGui.QIcon.fromTheme("close", QtGui.QIcon(":/icons/process-stop.svg"))
- )
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
dev_mode_active = pref.GetBool("developerMode", False)
# enable/disable stuff
- self.dialog.buttonUpdateAll.setEnabled(False)
+ self.button_bar.update_all_addons.setEnabled(False)
self.hide_progress_widgets()
- self.dialog.buttonUpdateCache.setEnabled(False)
- self.dialog.buttonUpdateCache.setText(translate("AddonsInstaller", "Starting up..."))
+ self.button_bar.refresh_local_cache.setEnabled(False)
+ self.button_bar.refresh_local_cache.setText(translate("AddonsInstaller", "Starting up..."))
if dev_mode_active:
- self.dialog.buttonDevTools.show()
+ self.button_bar.developer_tools.show()
else:
- self.dialog.buttonDevTools.hide()
+ self.button_bar.developer_tools.hide()
# connect slots
self.dialog.rejected.connect(self.reject)
- self.dialog.buttonUpdateAll.clicked.connect(self.update_all)
- self.dialog.buttonClose.clicked.connect(self.dialog.reject)
- self.dialog.buttonUpdateCache.clicked.connect(self.on_buttonUpdateCache_clicked)
- self.dialog.buttonCheckForUpdates.clicked.connect(
+ self.button_bar.update_all_addons.clicked.connect(self.update_all)
+ self.button_bar.close.clicked.connect(self.dialog.reject)
+ self.button_bar.refresh_local_cache.clicked.connect(self.on_buttonUpdateCache_clicked)
+ self.button_bar.check_for_updates.clicked.connect(
lambda: self.force_check_updates(standalone=True)
)
- self.dialog.buttonUpdateDependencies.clicked.connect(self.show_python_updates_dialog)
- self.dialog.buttonDevTools.clicked.connect(self.show_developer_tools)
+ self.button_bar.python_dependencies.clicked.connect(self.show_python_updates_dialog)
+ self.button_bar.developer_tools.clicked.connect(self.show_developer_tools)
self.packageList.ui.progressBar.stop_clicked.connect(self.stop_update)
self.packageList.itemSelected.connect(self.table_row_activated)
self.packageList.setEnabled(False)
@@ -415,8 +412,8 @@ class CommandAddonManager:
else:
self.hide_progress_widgets()
self.update_cache = False
- self.dialog.buttonUpdateCache.setEnabled(True)
- self.dialog.buttonUpdateCache.setText(
+ self.button_bar.refresh_local_cache.setEnabled(True)
+ self.button_bar.refresh_local_cache.setText(
translate("AddonsInstaller", "Refresh local cache")
)
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
@@ -545,8 +542,10 @@ class CommandAddonManager:
cache_path = FreeCAD.getUserCachePath()
am_path = os.path.join(cache_path, "AddonManager")
utils.rmdir(am_path)
- self.dialog.buttonUpdateCache.setEnabled(False)
- self.dialog.buttonUpdateCache.setText(translate("AddonsInstaller", "Updating cache..."))
+ self.button_bar.refresh_local_cache.setEnabled(False)
+ self.button_bar.refresh_local_cache.setText(
+ translate("AddonsInstaller", "Updating cache...")
+ )
self.startup()
# Recaching implies checking for updates, regardless of the user's autocheck option
@@ -608,10 +607,12 @@ class CommandAddonManager:
self.do_next_startup_phase()
return
- self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller", "Checking for updates..."))
+ self.button_bar.update_all_addons.setText(
+ translate("AddonsInstaller", "Checking for updates...")
+ )
self.packages_with_updates.clear()
- self.dialog.buttonUpdateAll.show()
- self.dialog.buttonCheckForUpdates.setDisabled(True)
+ self.button_bar.update_all_addons.show()
+ self.button_bar.check_for_updates.setDisabled(True)
self.check_worker = CheckWorkbenchesForUpdatesWorker(self.item_model.repos)
self.check_worker.finished.connect(self.do_next_startup_phase)
self.check_worker.finished.connect(self.update_check_complete)
@@ -636,21 +637,21 @@ class CommandAddonManager:
if number_of_updates:
s = translate("AddonsInstaller", "Apply {} update(s)", "", number_of_updates)
- self.dialog.buttonUpdateAll.setText(s.format(number_of_updates))
- self.dialog.buttonUpdateAll.setEnabled(True)
+ self.button_bar.update_all_addons.setText(s.format(number_of_updates))
+ self.button_bar.update_all_addons.setEnabled(True)
elif hasattr(self, "check_worker") and self.check_worker.isRunning():
- self.dialog.buttonUpdateAll.setText(
+ self.button_bar.update_all_addons.setText(
translate("AddonsInstaller", "Checking for updates...")
)
else:
- self.dialog.buttonUpdateAll.setText(
+ self.button_bar.update_all_addons.setText(
translate("AddonsInstaller", "No updates available")
)
- self.dialog.buttonUpdateAll.setEnabled(False)
+ self.button_bar.update_all_addons.setEnabled(False)
def update_check_complete(self) -> None:
self.enable_updates(len(self.packages_with_updates))
- self.dialog.buttonCheckForUpdates.setEnabled(True)
+ self.button_bar.check_for_updates.setEnabled(True)
def check_python_updates(self) -> None:
PythonPackageManager.migrate_old_am_installations() # Migrate 0.20 to 0.21
@@ -846,8 +847,10 @@ class CommandAddonManager:
self.cleanup_workers()
self.hide_progress_widgets()
self.write_cache_stopfile()
- self.dialog.buttonUpdateCache.setEnabled(True)
- self.dialog.buttonUpdateCache.setText(translate("AddonsInstaller", "Refresh local cache"))
+ self.button_bar.refresh_local_cache.setEnabled(True)
+ self.button_bar.refresh_local_cache.setText(
+ translate("AddonsInstaller", "Refresh local cache")
+ )
def write_cache_stopfile(self) -> None:
stopfile = utils.get_cache_file_name("CACHE_UPDATE_INTERRUPTED")
diff --git a/src/Mod/AddonManager/AddonManager.ui b/src/Mod/AddonManager/AddonManager.ui
index 8d394a589c..b2d31fcb15 100644
--- a/src/Mod/AddonManager/AddonManager.ui
+++ b/src/Mod/AddonManager/AddonManager.ui
@@ -13,95 +13,7 @@
Addon Manager
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- QLayout::SetDefaultConstraint
-
-
-
-
-
- Refresh local cache
-
-
-
- -
-
-
- Download and apply all available updates
-
-
- Update all Addons
-
-
-
- -
-
-
- Check for updates
-
-
-
- -
-
-
- true
-
-
- View and update Python package dependencies
-
-
- Python dependencies...
-
-
-
- -
-
-
- Developer tools...
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Close the Addon Manager
-
-
- Close
-
-
- true
-
-
-
-
-
-
+
diff --git a/src/Mod/AddonManager/Widgets/CMakeLists.txt b/src/Mod/AddonManager/Widgets/CMakeLists.txt
index 99b4ee4617..e3fefa9405 100644
--- a/src/Mod/AddonManager/Widgets/CMakeLists.txt
+++ b/src/Mod/AddonManager/Widgets/CMakeLists.txt
@@ -1,6 +1,7 @@
SET(AddonManagerWidget_SRCS
__init__.py
addonmanager_widget_filter_selector.py
+ addonmanager_widget_global_buttons.py
addonmanager_widget_progress_bar.py
addonmanager_widget_search.py
addonmanager_widget_view_control_bar.py
diff --git a/src/Mod/AddonManager/Widgets/addonmanager_widget_global_buttons.py b/src/Mod/AddonManager/Widgets/addonmanager_widget_global_buttons.py
new file mode 100644
index 0000000000..b175657b56
--- /dev/null
+++ b/src/Mod/AddonManager/Widgets/addonmanager_widget_global_buttons.py
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# ***************************************************************************
+# * *
+# * Copyright (c) 2024 FreeCAD Project Association *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * FreeCAD is distributed in the hope that it will be useful, but *
+# * WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************
+
+""" Defines a QWidget-derived class for displaying a set of buttons that affect the Addon
+Manager as a whole (rather than a specific Addon). Typically inserted at the bottom of the Addon
+Manager main window. """
+
+try:
+ import FreeCAD
+
+ translate = FreeCAD.Qt.translate
+except ImportError:
+ FreeCAD = None
+
+ def translate(_: str, text: str):
+ return text
+
+
+# Get whatever version of PySide we can
+try:
+ import PySide # Use the FreeCAD wrapper
+except ImportError:
+ try:
+ import PySide6 # Outside FreeCAD, try Qt6 first
+
+ PySide = PySide6
+ except ImportError:
+ import PySide2 # Fall back to Qt5 (if this fails, Python will kill this module's import)
+
+ PySide = PySide2
+
+from PySide import QtGui, QtWidgets
+
+
+class WidgetGlobalButtonBar(QtWidgets.QWidget):
+ """A QWidget-derived class for displaying a set of buttons that affect the Addon Manager as a
+ whole (rather than a specific Addon)."""
+
+ def __init__(self, parent: QtWidgets.QWidget = None):
+ super().__init__(parent)
+ self.horizontal_layout = None
+ self.refresh_local_cache = None
+ self.update_all_addons = None
+ self.check_for_updates = None
+ self.python_dependencies = None
+ self.developer_tools = None
+ self.close = None
+ self._update_ui()
+ self.retranslateUi(None)
+ self._set_icons()
+
+ def _update_ui(self):
+ self.horizontal_layout = QtWidgets.QHBoxLayout()
+ self.refresh_local_cache = QtWidgets.QPushButton(self)
+ self.update_all_addons = QtWidgets.QPushButton(self)
+ self.check_for_updates = QtWidgets.QPushButton(self)
+ self.python_dependencies = QtWidgets.QPushButton(self)
+ self.developer_tools = QtWidgets.QPushButton(self)
+ self.close = QtWidgets.QPushButton(self)
+ self.horizontal_layout.addWidget(self.refresh_local_cache)
+ self.horizontal_layout.addWidget(self.update_all_addons)
+ self.horizontal_layout.addWidget(self.check_for_updates)
+ self.horizontal_layout.addWidget(self.python_dependencies)
+ self.horizontal_layout.addWidget(self.developer_tools)
+ self.horizontal_layout.addStretch()
+ self.horizontal_layout.addWidget(self.close)
+ self.setLayout(self.horizontal_layout)
+
+ def _set_icons(self):
+ self.update_all_addons.setIcon(QtGui.QIcon(":/icons/button_valid.svg"))
+ self.check_for_updates.setIcon(QtGui.QIcon(":/icons/view-refresh.svg"))
+ self.close.setIcon(QtGui.QIcon.fromTheme("close", QtGui.QIcon(":/icons/process-stop.svg")))
+
+ def retranslateUi(self, _):
+ self.refresh_local_cache.setText(translate("AddonsInstaller", "Close"))
+ self.update_all_addons.setText(translate("AddonsInstaller", "Update all addons"))
+ self.check_for_updates.setText(translate("AddonsInstaller", "Check for updates"))
+ self.python_dependencies.setText(translate("AddonsInstaller", "Python dependencies..."))
+ self.developer_tools.setText(translate("AddonsInstaller", "Developer tools..."))
+ self.close.setText(translate("AddonsInstaller", "Close"))