AYON Launcher tool: Fix refresh btn (#5685)

* rename 'refresh' to 'set_context' in 'TasksModel'

* implemented 'refresh' for folders and tasks widgets

* propagate refresh to all widgets

* don't use 'clear' of 'QStandardItemModel'

* change lifetime of folders cache to a minute

* added 'refresh_actions' method to launcher to skip clear cache of folders

* shorten line

* sorting is not case sensitive
This commit is contained in:
Jakub Trllo 2023-10-06 10:44:39 +02:00 committed by GitHub
parent 12f4128901
commit 2ea8d6530f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 124 additions and 42 deletions

View file

@ -295,3 +295,13 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon):
"""
pass
@abstractmethod
def refresh_actions(self):
"""Refresh actions and all related data.
Triggers 'controller.refresh.actions.started' event at the beginning
and 'controller.refresh.actions.finished' at the end.
"""
pass

View file

@ -145,5 +145,17 @@ class BaseLauncherController(
self._emit_event("controller.refresh.finished")
def refresh_actions(self):
self._emit_event("controller.refresh.actions.started")
# Refresh project settings (used for actions discovery)
self._project_settings = {}
# Refresh projects - they define applications
self._projects_model.reset()
# Refresh actions
self._actions_model.refresh()
self._emit_event("controller.refresh.actions.finished")
def _emit_event(self, topic, data=None):
self.emit_event(topic, data, "controller")

View file

@ -46,10 +46,6 @@ class ActionsQtModel(QtGui.QStandardItemModel):
def __init__(self, controller):
super(ActionsQtModel, self).__init__()
controller.register_event_callback(
"controller.refresh.finished",
self._on_controller_refresh_finished,
)
controller.register_event_callback(
"selection.project.changed",
self._on_selection_project_changed,
@ -170,13 +166,6 @@ class ActionsQtModel(QtGui.QStandardItemModel):
self._action_items_by_id = action_items_by_id
self.refreshed.emit()
def _on_controller_refresh_finished(self):
context = self._controller.get_selected_context()
self._selected_project_name = context["project_name"]
self._selected_folder_id = context["folder_id"]
self._selected_task_id = context["task_id"]
self.refresh()
def _on_selection_project_changed(self, event):
self._selected_project_name = event["project_name"]
self._selected_folder_id = None
@ -361,6 +350,9 @@ class ActionsWidget(QtWidgets.QWidget):
self._set_row_height(1)
def refresh(self):
self._model.refresh()
def _set_row_height(self, rows):
self.setMinimumHeight(rows * 75)

View file

@ -92,6 +92,10 @@ class HierarchyPage(QtWidgets.QWidget):
if visible and project_name:
self._projects_combobox.set_selection(project_name)
def refresh(self):
self._folders_widget.refresh()
self._tasks_widget.refresh()
def _on_back_clicked(self):
self._controller.set_selected_project(None)

View file

@ -73,6 +73,9 @@ class ProjectIconView(QtWidgets.QListView):
class ProjectsWidget(QtWidgets.QWidget):
"""Projects Page"""
refreshed = QtCore.Signal()
def __init__(self, controller, parent=None):
super(ProjectsWidget, self).__init__(parent=parent)
@ -104,6 +107,7 @@ class ProjectsWidget(QtWidgets.QWidget):
main_layout.addWidget(projects_view, 1)
projects_view.clicked.connect(self._on_view_clicked)
projects_model.refreshed.connect(self.refreshed)
projects_filter_text.textChanged.connect(
self._on_project_filter_change)
refresh_btn.clicked.connect(self._on_refresh_clicked)
@ -119,6 +123,15 @@ class ProjectsWidget(QtWidgets.QWidget):
self._projects_model = projects_model
self._projects_proxy_model = projects_proxy_model
def has_content(self):
"""Model has at least one project.
Returns:
bool: True if there is any content in the model.
"""
return self._projects_model.has_content()
def _on_view_clicked(self, index):
if index.isValid():
project_name = index.data(QtCore.Qt.DisplayRole)

View file

@ -99,8 +99,8 @@ class LauncherWindow(QtWidgets.QWidget):
message_timer.setInterval(self.message_interval)
message_timer.setSingleShot(True)
refresh_timer = QtCore.QTimer()
refresh_timer.setInterval(self.refresh_interval)
actions_refresh_timer = QtCore.QTimer()
actions_refresh_timer.setInterval(self.refresh_interval)
page_slide_anim = QtCore.QVariantAnimation(self)
page_slide_anim.setDuration(self.page_side_anim_interval)
@ -108,8 +108,10 @@ class LauncherWindow(QtWidgets.QWidget):
page_slide_anim.setEndValue(1.0)
page_slide_anim.setEasingCurve(QtCore.QEasingCurve.OutQuad)
projects_page.refreshed.connect(self._on_projects_refresh)
message_timer.timeout.connect(self._on_message_timeout)
refresh_timer.timeout.connect(self._on_refresh_timeout)
actions_refresh_timer.timeout.connect(
self._on_actions_refresh_timeout)
page_slide_anim.valueChanged.connect(
self._on_page_slide_value_changed)
page_slide_anim.finished.connect(self._on_page_slide_finished)
@ -132,6 +134,7 @@ class LauncherWindow(QtWidgets.QWidget):
self._is_on_projects_page = True
self._window_is_active = False
self._refresh_on_activate = False
self._selected_project_name = None
self._pages_widget = pages_widget
self._pages_layout = pages_layout
@ -143,7 +146,7 @@ class LauncherWindow(QtWidgets.QWidget):
# self._action_history = action_history
self._message_timer = message_timer
self._refresh_timer = refresh_timer
self._actions_refresh_timer = actions_refresh_timer
self._page_slide_anim = page_slide_anim
hierarchy_page.setVisible(not self._is_on_projects_page)
@ -152,14 +155,14 @@ class LauncherWindow(QtWidgets.QWidget):
def showEvent(self, event):
super(LauncherWindow, self).showEvent(event)
self._window_is_active = True
if not self._refresh_timer.isActive():
self._refresh_timer.start()
if not self._actions_refresh_timer.isActive():
self._actions_refresh_timer.start()
self._controller.refresh()
def closeEvent(self, event):
super(LauncherWindow, self).closeEvent(event)
self._window_is_active = False
self._refresh_timer.stop()
self._actions_refresh_timer.stop()
def changeEvent(self, event):
if event.type() in (
@ -170,15 +173,15 @@ class LauncherWindow(QtWidgets.QWidget):
self._window_is_active = is_active
if is_active and self._refresh_on_activate:
self._refresh_on_activate = False
self._on_refresh_timeout()
self._refresh_timer.start()
self._on_actions_refresh_timeout()
self._actions_refresh_timer.start()
super(LauncherWindow, self).changeEvent(event)
def _on_refresh_timeout(self):
def _on_actions_refresh_timeout(self):
# Stop timer if widget is not visible
if self._window_is_active:
self._controller.refresh()
self._controller.refresh_actions()
else:
self._refresh_on_activate = True
@ -191,12 +194,26 @@ class LauncherWindow(QtWidgets.QWidget):
def _on_project_selection_change(self, event):
project_name = event["project_name"]
self._selected_project_name = project_name
if not project_name:
self._go_to_projects_page()
elif self._is_on_projects_page:
self._go_to_hierarchy_page(project_name)
def _on_projects_refresh(self):
# There is nothing to do, we're on projects page
if self._is_on_projects_page:
return
# No projects were found -> go back to projects page
if not self._projects_page.has_content():
self._go_to_projects_page()
return
self._hierarchy_page.refresh()
self._actions_widget.refresh()
def _on_action_trigger_started(self, event):
self._echo("Running action: {}".format(event["full_label"]))

View file

@ -199,13 +199,18 @@ class HierarchyModel(object):
Hierarchy items are folders and tasks. Folders can have as parent another
folder or project. Tasks can have as parent only folder.
"""
lifetime = 60 # A minute
def __init__(self, controller):
self._folders_items = NestedCacheItem(levels=1, default_factory=dict)
self._folders_by_id = NestedCacheItem(levels=2, default_factory=dict)
self._folders_items = NestedCacheItem(
levels=1, default_factory=dict, lifetime=self.lifetime)
self._folders_by_id = NestedCacheItem(
levels=2, default_factory=dict, lifetime=self.lifetime)
self._task_items = NestedCacheItem(levels=2, default_factory=dict)
self._tasks_by_id = NestedCacheItem(levels=2, default_factory=dict)
self._task_items = NestedCacheItem(
levels=2, default_factory=dict, lifetime=self.lifetime)
self._tasks_by_id = NestedCacheItem(
levels=2, default_factory=dict, lifetime=self.lifetime)
self._folders_refreshing = set()
self._tasks_refreshing = set()

View file

@ -56,11 +56,21 @@ class FoldersModel(QtGui.QStandardItemModel):
return self._has_content
def clear(self):
def refresh(self):
"""Refresh folders for last selected project.
Force to update folders model from controller. This may or may not
trigger query from server, that's based on controller's cache.
"""
self.set_project_name(self._last_project_name)
def _clear_items(self):
self._items_by_id = {}
self._parent_id_by_id = {}
self._has_content = False
super(FoldersModel, self).clear()
root_item = self.invisibleRootItem()
root_item.removeRows(0, root_item.rowCount())
def get_index_by_id(self, item_id):
"""Get index by folder id.
@ -90,7 +100,7 @@ class FoldersModel(QtGui.QStandardItemModel):
self._is_refreshing = True
if self._last_project_name != project_name:
self.clear()
self._clear_items()
self._last_project_name = project_name
thread = self._refresh_threads.get(project_name)
@ -135,7 +145,7 @@ class FoldersModel(QtGui.QStandardItemModel):
def _fill_items(self, folder_items_by_id):
if not folder_items_by_id:
if folder_items_by_id is not None:
self.clear()
self._clear_items()
self._is_refreshing = False
self.refreshed.emit()
return
@ -247,6 +257,7 @@ class FoldersWidget(QtWidgets.QWidget):
folders_model = FoldersModel(controller)
folders_proxy_model = RecursiveSortFilterProxyModel()
folders_proxy_model.setSourceModel(folders_model)
folders_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
folders_view.setModel(folders_proxy_model)
@ -293,6 +304,14 @@ class FoldersWidget(QtWidgets.QWidget):
self._folders_proxy_model.setFilterFixedString(name)
def refresh(self):
"""Refresh folders model.
Force to update folders model from controller.
"""
self._folders_model.refresh()
def _on_project_selection_change(self, event):
project_name = event["project_name"]
self._set_project_name(project_name)
@ -300,9 +319,6 @@ class FoldersWidget(QtWidgets.QWidget):
def _set_project_name(self, project_name):
self._folders_model.set_project_name(project_name)
def _clear(self):
self._folders_model.clear()
def _on_folders_refresh_finished(self, event):
if event["sender"] != SENDER_NAME:
self._set_project_name(event["project_name"])

View file

@ -44,14 +44,20 @@ class TasksModel(QtGui.QStandardItemModel):
# Initial state
self._add_invalid_selection_item()
def clear(self):
def _clear_items(self):
self._items_by_name = {}
self._has_content = False
self._remove_invalid_items()
super(TasksModel, self).clear()
root_item = self.invisibleRootItem()
root_item.removeRows(0, root_item.rowCount())
def refresh(self, project_name, folder_id):
"""Refresh tasks for folder.
def refresh(self):
"""Refresh tasks for last project and folder."""
self._refresh(self._last_project_name, self._last_folder_id)
def set_context(self, project_name, folder_id):
"""Set context for which should be tasks showed.
Args:
project_name (Union[str]): Name of project.
@ -121,7 +127,7 @@ class TasksModel(QtGui.QStandardItemModel):
return self._empty_tasks_item
def _add_invalid_item(self, item):
self.clear()
self._clear_items()
root_item = self.invisibleRootItem()
root_item.appendRow(item)
@ -299,6 +305,7 @@ class TasksWidget(QtWidgets.QWidget):
tasks_model = TasksModel(controller)
tasks_proxy_model = QtCore.QSortFilterProxyModel()
tasks_proxy_model.setSourceModel(tasks_model)
tasks_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
tasks_view.setModel(tasks_proxy_model)
@ -334,8 +341,14 @@ class TasksWidget(QtWidgets.QWidget):
self._handle_expected_selection = handle_expected_selection
self._expected_selection_data = None
def _clear(self):
self._tasks_model.clear()
def refresh(self):
"""Refresh folders for last selected project.
Force to update folders model from controller. This may or may not
trigger query from server, that's based on controller's cache.
"""
self._tasks_model.refresh()
def _on_tasks_refresh_finished(self, event):
"""Tasks were refreshed in controller.
@ -353,13 +366,13 @@ class TasksWidget(QtWidgets.QWidget):
or event["folder_id"] != self._selected_folder_id
):
return
self._tasks_model.refresh(
self._tasks_model.set_context(
event["project_name"], self._selected_folder_id
)
def _folder_selection_changed(self, event):
self._selected_folder_id = event["folder_id"]
self._tasks_model.refresh(
self._tasks_model.set_context(
event["project_name"], self._selected_folder_id
)