Skip to content

Commit

Permalink
Addon Manager: Refactor progress reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
chennes committed Dec 6, 2024
1 parent 63ad7d3 commit 02c7c27
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 93 deletions.
49 changes: 18 additions & 31 deletions src/Mod/AddonManager/AddonManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import AddonManager_rc # pylint: disable=unused-import
from composite_view import CompositeView
from Widgets.addonmanager_widget_global_buttons import WidgetGlobalButtonBar
from Widgets.addonmanager_widget_progress_bar import Progress
from package_list import PackageListItemModel
from Addon import Addon
from manage_python_dependencies import (
Expand Down Expand Up @@ -310,7 +311,7 @@ def launch(self) -> None:
)
self.button_bar.python_dependencies.clicked.connect(self.show_python_updates_dialog)
self.button_bar.developer_tools.clicked.connect(self.show_developer_tools)
self.composite_view.package_list.ui.progressBar.stop_clicked.connect(self.stop_update)
self.composite_view.package_list.stop_loading.connect(self.stop_update)
self.composite_view.package_list.setEnabled(False)
self.composite_view.execute.connect(self.execute_macro)
self.composite_view.install.connect(self.launch_installer_gui)
Expand All @@ -328,9 +329,6 @@ def launch(self) -> None:
# begin populating the table in a set of sub-threads
self.startup()

# set the label text to start with
self.show_information(translate("AddonsInstaller", "Loading addon information"))

# rock 'n roll!!!
self.dialog.exec()

Expand Down Expand Up @@ -522,9 +520,8 @@ def populate_packages_table(self) -> None:
self.update_cache = True # Make sure to trigger the other cache updates, if the json
# file was missing
self.create_addon_list_worker = CreateAddonListWorker()
self.create_addon_list_worker.status_message.connect(self.show_information)
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(10, 100)
self.update_progress_bar(translate("AddonsInstaller", "Creating addon list"), 10, 100)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
Expand All @@ -534,7 +531,7 @@ def populate_packages_table(self) -> None:
utils.get_cache_file_name("package_cache.json")
)
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(10, 100)
self.update_progress_bar(translate("AddonsInstaller", "Loading addon list"), 10, 100)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
Expand Down Expand Up @@ -568,9 +565,10 @@ def populate_macros(self) -> None:
self.update_cache = True # Make sure to trigger the other cache updates, if the
# json file was missing
self.create_addon_list_worker = CreateAddonListWorker()
self.create_addon_list_worker.status_message.connect(self.show_information)
self.create_addon_list_worker.addon_repo.connect(self.add_addon_repo)
self.update_progress_bar(10, 100)
self.update_progress_bar(
translate("AddonsInstaller", "Creating macro list"), 10, 100
)
self.create_addon_list_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 2
Expand Down Expand Up @@ -608,7 +606,6 @@ def write_macro_cache(self):
def update_metadata_cache(self) -> None:
if self.update_cache:
self.update_metadata_cache_worker = UpdateMetadataCacheWorker(self.item_model.repos)
self.update_metadata_cache_worker.status_message.connect(self.show_information)
self.update_metadata_cache_worker.finished.connect(
self.do_next_startup_phase
) # Link to step 4
Expand Down Expand Up @@ -644,7 +641,6 @@ def on_package_updated(self, repo: Addon) -> None:
def load_macro_metadata(self) -> None:
if self.update_cache:
self.load_macro_metadata_worker = CacheMacroCodeWorker(self.item_model.repos)
self.load_macro_metadata_worker.status_message.connect(self.show_information)
self.load_macro_metadata_worker.update_macro.connect(self.on_package_updated)
self.load_macro_metadata_worker.progress_made.connect(self.update_progress_bar)
self.load_macro_metadata_worker.finished.connect(self.do_next_startup_phase)
Expand Down Expand Up @@ -798,12 +794,6 @@ def add_addon_repo(self, addon_repo: Addon) -> None:
return
self.item_model.append_item(addon_repo)

def show_information(self, message: str) -> None:
"""shows generic text in the information pane"""

self.composite_view.package_list.ui.progressBar.set_status(message)
self.composite_view.package_list.ui.progressBar.repaint()

def append_to_repos_list(self, repo: Addon) -> None:
"""this function allows threads to update the main list of workbenches"""
self.item_model.append_item(repo)
Expand Down Expand Up @@ -862,15 +852,12 @@ def update_all(self) -> None:

def hide_progress_widgets(self) -> None:
"""hides the progress bar and related widgets"""

self.composite_view.package_list.ui.progressBar.hide()
self.composite_view.package_list.ui.view_bar.search.setFocus()
self.composite_view.package_list.set_loading(False)

def show_progress_widgets(self) -> None:
if self.composite_view.package_list.ui.progressBar.isHidden():
self.composite_view.package_list.ui.progressBar.show()
self.composite_view.package_list.set_loading(True)

def update_progress_bar(self, current_value: int, max_value: int) -> None:
def update_progress_bar(self, message: str, current_value: int, max_value: int) -> None:
"""Update the progress bar, showing it if it's hidden"""

max_value = max_value if max_value > 0 else 1
Expand All @@ -881,14 +868,14 @@ def update_progress_bar(self, current_value: int, max_value: int) -> None:
current_value = max_value

self.show_progress_widgets()
region_size = 100.0 / self.number_of_progress_regions
completed_region_portion = (self.current_progress_region - 1) * region_size
current_region_portion = (float(current_value) / float(max_value)) * region_size
value = completed_region_portion + current_region_portion
self.composite_view.package_list.ui.progressBar.set_value(
value * 10
) # Out of 1000 segments, so it moves sort of smoothly
self.composite_view.package_list.ui.progressBar.repaint()

progress = Progress(
status_text=message,
number_of_tasks=self.number_of_progress_regions,
current_task=self.current_progress_region - 1,
current_task_progress=current_value / max_value,
)
self.composite_view.package_list.update_loading_progress(progress)

def stop_update(self) -> None:
self.cleanup_workers()
Expand Down
145 changes: 145 additions & 0 deletions src/Mod/AddonManager/AddonManagerTest/gui/test_widget_progress_bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022 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 *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************

import sys
import unittest

sys.path.append("../..")

from Widgets.addonmanager_widget_progress_bar import Progress


class TestProgress(unittest.TestCase):

def test_default_construction(self):
"""Given no parameters, a single-task Progress object is initialized with zero progress"""
progress = Progress()
self.assertEqual(progress.status_text, "")
self.assertEqual(progress.number_of_tasks, 1)
self.assertEqual(progress.current_task, 0)
self.assertEqual(progress.current_task_progress, 0.0)

def test_good_parameters(self):
"""Given good parameters, no exception is raised"""
_ = Progress(
status_text="Some text", number_of_tasks=1, current_task=0, current_task_progress=0.0
)

def test_zero_task_count(self):
with self.assertRaises(ValueError):
_ = Progress(number_of_tasks=0)

def test_negative_task_count(self):
with self.assertRaises(ValueError):
_ = Progress(number_of_tasks=-1)

def test_setting_status_post_creation(self):
progress = Progress()
self.assertEqual(progress.status_text, "")
progress.status_text = "Some status"
self.assertEqual(progress.status_text, "Some status")

def test_setting_task_count(self):
progress = Progress()
progress.number_of_tasks = 10
self.assertEqual(progress.number_of_tasks, 10)

def test_setting_negative_task_count(self):
progress = Progress()
with self.assertRaises(ValueError):
progress.number_of_tasks = -1

def test_setting_invalid_task_count(self):
progress = Progress()
with self.assertRaises(TypeError):
progress.number_of_tasks = 3.14159

def test_setting_current_task(self):
progress = Progress(number_of_tasks=10)
progress.number_of_tasks = 5
self.assertEqual(progress.number_of_tasks, 5)

def test_setting_current_task_greater_than_task_count(self):
progress = Progress()
progress.number_of_tasks = 10
with self.assertRaises(ValueError):
progress.current_task = 11

def test_setting_current_task_equal_to_task_count(self):
"""current_task is zero-indexed, so this is too high"""
progress = Progress()
progress.number_of_tasks = 10
with self.assertRaises(ValueError):
progress.current_task = 10

def test_setting_current_task_negative(self):
progress = Progress()
with self.assertRaises(ValueError):
progress.current_task = -1

def test_setting_current_task_invalid(self):
progress = Progress()
with self.assertRaises(TypeError):
progress.current_task = 2.718281

def test_setting_current_task_progress(self):
progress = Progress()
progress.current_task_progress = 50.0
self.assertEqual(progress.current_task_progress, 50.0)

def test_setting_current_task_progress_too_low(self):
progress = Progress()
progress.current_task_progress = -0.01
self.assertEqual(progress.current_task_progress, 0.0)

def test_setting_current_task_progress_too_high(self):
progress = Progress()
progress.current_task_progress = 100.001
self.assertEqual(progress.current_task_progress, 100.0)

def test_incrementing_task(self):
progress = Progress(number_of_tasks=10, current_task_progress=100.0)
progress.next_task()
self.assertEqual(progress.current_task, 1)
self.assertEqual(progress.current_task_progress, 0.0)

def test_incrementing_task_too_high(self):
progress = Progress(number_of_tasks=10, current_task=9, current_task_progress=100.0)
with self.assertRaises(ValueError):
progress.next_task()

def test_overall_progress_simple(self):
progress = Progress()
self.assertEqual(progress.overall_progress(), 0.0)

def test_overall_progress_with_ranges(self):
progress = Progress(number_of_tasks=2, current_task=1, current_task_progress=0.0)
self.assertAlmostEqual(progress.overall_progress(), 0.5)

def test_overall_progress_with_ranges_and_progress(self):
progress = Progress(number_of_tasks=10, current_task=5, current_task_progress=50.0)
self.assertAlmostEqual(progress.overall_progress(), 0.55)


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 02c7c27

Please sign in to comment.