Fix up documentation of WelcomePageModel

Some more boyscouting

CURA-8696
This commit is contained in:
Jaime van Kessel 2021-12-01 10:29:31 +01:00
parent 6bce9453e4
commit 051c3ee2e2

View file

@ -1,7 +1,9 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from collections import deque
import os import os
from collections import deque
from typing import TYPE_CHECKING, Optional, List, Dict, Any from typing import TYPE_CHECKING, Optional, List, Dict, Any
from PyQt5.QtCore import QUrl, Qt, pyqtSlot, pyqtProperty, pyqtSignal from PyQt5.QtCore import QUrl, Qt, pyqtSlot, pyqtProperty, pyqtSignal
@ -16,24 +18,23 @@ if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
#
# This is the Qt ListModel that contains all welcome pages data. Each page is a page that can be shown as a step in the
# welcome wizard dialog. Each item in this ListModel represents a page, which contains the following fields:
#
# - id : A unique page_id which can be used in function goToPage(page_id)
# - page_url : The QUrl to the QML file that contains the content of this page
# - next_page_id : (OPTIONAL) The next page ID to go to when this page finished. This is optional. If this is not
# provided, it will go to the page with the current index + 1
# - next_page_button_text: (OPTIONAL) The text to show for the "next" button, by default it's the translated text of
# "Next". Note that each step QML can decide whether to use this text or not, so it's not
# mandatory.
# - should_show_function : (OPTIONAL) An optional function that returns True/False indicating if this page should be
# shown. By default all pages should be shown. If a function returns False, that page will
# be skipped and its next page will be shown.
#
# Note that in any case, a page that has its "should_show_function" == False will ALWAYS be skipped.
#
class WelcomePagesModel(ListModel): class WelcomePagesModel(ListModel):
"""
This is the Qt ListModel that contains all welcome pages data. Each page is a page that can be shown as a step in
the welcome wizard dialog. Each item in this ListModel represents a page, which contains the following fields:
- id : A unique page_id which can be used in function goToPage(page_id)
- page_url : The QUrl to the QML file that contains the content of this page
- next_page_id : (OPTIONAL) The next page ID to go to when this page finished. This is optional. If this is
not provided, it will go to the page with the current index + 1
- next_page_button_text : (OPTIONAL) The text to show for the "next" button, by default it's the translated text of
"Next". Note that each step QML can decide whether to use this text or not, so it's not
mandatory.
- should_show_function : (OPTIONAL) An optional function that returns True/False indicating if this page should be
shown. By default all pages should be shown. If a function returns False, that page will
be skipped and its next page will be shown.
Note that in any case, a page that has its "should_show_function" == False will ALWAYS be skipped.
"""
IdRole = Qt.UserRole + 1 # Page ID IdRole = Qt.UserRole + 1 # Page ID
PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file
@ -55,11 +56,11 @@ class WelcomePagesModel(ListModel):
self._default_next_button_text = self._catalog.i18nc("@action:button", "Next") self._default_next_button_text = self._catalog.i18nc("@action:button", "Next")
self._pages = [] # type: List[Dict[str, Any]] self._pages: List[Dict[str, Any]] = []
self._current_page_index = 0 self._current_page_index = 0
# Store all the previous page indices so it can go back. # Store all the previous page indices so it can go back.
self._previous_page_indices_stack = deque() # type: deque self._previous_page_indices_stack: deque = deque()
# If the welcome flow should be shown. It can show the complete flow or just the changelog depending on the # If the welcome flow should be shown. It can show the complete flow or just the changelog depending on the
# specific case. See initialize() for how this variable is set. # specific case. See initialize() for how this variable is set.
@ -72,17 +73,21 @@ class WelcomePagesModel(ListModel):
def currentPageIndex(self) -> int: def currentPageIndex(self) -> int:
return self._current_page_index return self._current_page_index
# Returns a float number in [0, 1] which indicates the current progress.
@pyqtProperty(float, notify = currentPageIndexChanged) @pyqtProperty(float, notify = currentPageIndexChanged)
def currentProgress(self) -> float: def currentProgress(self) -> float:
"""
Returns a float number in [0, 1] which indicates the current progress.
"""
if len(self._items) == 0: if len(self._items) == 0:
return 0 return 0
else: else:
return self._current_page_index / len(self._items) return self._current_page_index / len(self._items)
# Indicates if the current page is the last page.
@pyqtProperty(bool, notify = currentPageIndexChanged) @pyqtProperty(bool, notify = currentPageIndexChanged)
def isCurrentPageLast(self) -> bool: def isCurrentPageLast(self) -> bool:
"""
Indicates if the current page is the last page.
"""
return self._current_page_index == len(self._items) - 1 return self._current_page_index == len(self._items) - 1
def _setCurrentPageIndex(self, page_index: int) -> None: def _setCurrentPageIndex(self, page_index: int) -> None:
@ -91,17 +96,22 @@ class WelcomePagesModel(ListModel):
self._current_page_index = page_index self._current_page_index = page_index
self.currentPageIndexChanged.emit() self.currentPageIndexChanged.emit()
# Ends the Welcome-Pages. Put as a separate function for cases like the 'decline' in the User-Agreement.
@pyqtSlot() @pyqtSlot()
def atEnd(self) -> None: def atEnd(self) -> None:
"""
Ends the Welcome-Pages. Put as a separate function for cases like the 'decline' in the User-Agreement.
"""
self.allFinished.emit() self.allFinished.emit()
self.resetState() self.resetState()
# Goes to the next page.
# If "from_index" is given, it will look for the next page to show starting from the "from_index" page instead of
# the "self._current_page_index".
@pyqtSlot() @pyqtSlot()
def goToNextPage(self, from_index: Optional[int] = None) -> None: def goToNextPage(self, from_index: Optional[int] = None) -> None:
"""
Goes to the next page.
If "from_index" is given, it will look for the next page to show starting from the "from_index" page instead of
the "self._current_page_index".
"""
# Look for the next page that should be shown # Look for the next page that should be shown
current_index = self._current_page_index if from_index is None else from_index current_index = self._current_page_index if from_index is None else from_index
while True: while True:
@ -137,9 +147,11 @@ class WelcomePagesModel(ListModel):
# Move to the next page # Move to the next page
self._setCurrentPageIndex(next_page_index) self._setCurrentPageIndex(next_page_index)
# Goes to the previous page. If there's no previous page, do nothing.
@pyqtSlot() @pyqtSlot()
def goToPreviousPage(self) -> None: def goToPreviousPage(self) -> None:
"""
Goes to the previous page. If there's no previous page, do nothing.
"""
if len(self._previous_page_indices_stack) == 0: if len(self._previous_page_indices_stack) == 0:
Logger.log("i", "No previous page, do nothing") Logger.log("i", "No previous page, do nothing")
return return
@ -148,9 +160,9 @@ class WelcomePagesModel(ListModel):
self._current_page_index = previous_page_index self._current_page_index = previous_page_index
self.currentPageIndexChanged.emit() self.currentPageIndexChanged.emit()
# Sets the current page to the given page ID. If the page ID is not found, do nothing.
@pyqtSlot(str) @pyqtSlot(str)
def goToPage(self, page_id: str) -> None: def goToPage(self, page_id: str) -> None:
"""Sets the current page to the given page ID. If the page ID is not found, do nothing."""
page_index = self.getPageIndexById(page_id) page_index = self.getPageIndexById(page_id)
if page_index is None: if page_index is None:
# FIXME: If we cannot find the next page, we cannot do anything here. # FIXME: If we cannot find the next page, we cannot do anything here.
@ -165,18 +177,22 @@ class WelcomePagesModel(ListModel):
# Find the next page to show starting from the "page_index" # Find the next page to show starting from the "page_index"
self.goToNextPage(from_index = page_index) self.goToNextPage(from_index = page_index)
# Checks if the page with the given index should be shown by calling the "should_show_function" associated with it.
# If the function is not present, returns True (show page by default).
def _shouldPageBeShown(self, page_index: int) -> bool: def _shouldPageBeShown(self, page_index: int) -> bool:
"""
Checks if the page with the given index should be shown by calling the "should_show_function" associated with
it. If the function is not present, returns True (show page by default).
"""
next_page_item = self.getItem(page_index) next_page_item = self.getItem(page_index)
should_show_function = next_page_item.get("should_show_function", lambda: True) should_show_function = next_page_item.get("should_show_function", lambda: True)
return should_show_function() return should_show_function()
# Resets the state of the WelcomePagesModel. This functions does the following:
# - Resets current_page_index to 0
# - Clears the previous page indices stack
@pyqtSlot() @pyqtSlot()
def resetState(self) -> None: def resetState(self) -> None:
"""
Resets the state of the WelcomePagesModel. This functions does the following:
- Resets current_page_index to 0
- Clears the previous page indices stack
"""
self._current_page_index = 0 self._current_page_index = 0
self._previous_page_indices_stack.clear() self._previous_page_indices_stack.clear()
@ -188,8 +204,8 @@ class WelcomePagesModel(ListModel):
def shouldShowWelcomeFlow(self) -> bool: def shouldShowWelcomeFlow(self) -> bool:
return self._should_show_welcome_flow return self._should_show_welcome_flow
# Gets the page index with the given page ID. If the page ID doesn't exist, returns None.
def getPageIndexById(self, page_id: str) -> Optional[int]: def getPageIndexById(self, page_id: str) -> Optional[int]:
"""Gets the page index with the given page ID. If the page ID doesn't exist, returns None."""
page_idx = None page_idx = None
for idx, page_item in enumerate(self._items): for idx, page_item in enumerate(self._items):
if page_item["id"] == page_id: if page_item["id"] == page_id:
@ -197,8 +213,8 @@ class WelcomePagesModel(ListModel):
break break
return page_idx return page_idx
# Convenience function to get QUrl path to pages that's located in "resources/qml/WelcomePages".
def _getBuiltinWelcomePagePath(self, page_filename: str) -> "QUrl": def _getBuiltinWelcomePagePath(self, page_filename: str) -> "QUrl":
"""Convenience function to get QUrl path to pages that's located in "resources/qml/WelcomePages"."""
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
return QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, return QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles,
os.path.join("WelcomePages", page_filename))) os.path.join("WelcomePages", page_filename)))
@ -223,10 +239,11 @@ class WelcomePagesModel(ListModel):
show_whatsnew_only = has_active_machine and has_app_just_upgraded show_whatsnew_only = has_active_machine and has_app_just_upgraded
# FIXME: This is a hack. Because of the circular dependency between MachineManager, ExtruderManager, and # FIXME: This is a hack. Because of the circular dependency between MachineManager, ExtruderManager, and
# possibly some others, setting the initial active machine is not done when the MachineManager gets initialized. # possibly some others, setting the initial active machine is not done when the MachineManager gets
# So at this point, we don't know if there will be an active machine or not. It could be that the active machine # initialized. So at this point, we don't know if there will be an active machine or not. It could be that
# files are corrupted so we cannot rely on Preferences either. This makes sure that once the active machine # the active machine files are corrupted so we cannot rely on Preferences either. This makes sure that once
# gets changed, this model updates the flags, so it can decide whether to show the welcome flow or not. # the active machine gets changed, this model updates the flags, so it can decide whether to show the
# welcome flow or not.
should_show_welcome_flow = show_complete_flow or show_whatsnew_only should_show_welcome_flow = show_complete_flow or show_whatsnew_only
if should_show_welcome_flow != self._should_show_welcome_flow: if should_show_welcome_flow != self._should_show_welcome_flow:
self._should_show_welcome_flow = should_show_welcome_flow self._should_show_welcome_flow = should_show_welcome_flow
@ -280,17 +297,19 @@ class WelcomePagesModel(ListModel):
self._pages = pages_to_show self._pages = pages_to_show
self.setItems(self._pages) self.setItems(self._pages)
# For convenience, inject the default "next" button text to each item if it's not present.
def setItems(self, items: List[Dict[str, Any]]) -> None: def setItems(self, items: List[Dict[str, Any]]) -> None:
# For convenience, inject the default "next" button text to each item if it's not present.
for item in items: for item in items:
if "next_page_button_text" not in item: if "next_page_button_text" not in item:
item["next_page_button_text"] = self._default_next_button_text item["next_page_button_text"] = self._default_next_button_text
super().setItems(items) super().setItems(items)
# Indicates if the machine action panel should be shown by checking if there's any first start machine actions
# available.
def shouldShowMachineActions(self) -> bool: def shouldShowMachineActions(self) -> bool:
"""
Indicates if the machine action panel should be shown by checking if there's any first start machine actions
available.
"""
global_stack = self._application.getMachineManager().activeMachine global_stack = self._application.getMachineManager().activeMachine
if global_stack is None: if global_stack is None:
return False return False
@ -312,6 +331,3 @@ class WelcomePagesModel(ListModel):
def addPage(self) -> None: def addPage(self) -> None:
pass pass
__all__ = ["WelcomePagesModel"]