Addon Manager: Break up ReadmeViewer into view and controller
Addon Manager: Cleanup enable/disable message
This commit is contained in:
committed by
Chris Hennes
parent
20a01cfc9c
commit
9812548b68
@@ -1,9 +1,12 @@
|
||||
SET(AddonManagerWidget_SRCS
|
||||
__init__.py
|
||||
addonmanager_colors.py
|
||||
addonmanager_widget_addon_buttons.py
|
||||
addonmanager_widget_filter_selector.py
|
||||
addonmanager_widget_global_buttons.py
|
||||
addonmanager_widget_package_details_view.py
|
||||
addonmanager_widget_progress_bar.py
|
||||
addonmanager_widget_readme_browser.py
|
||||
addonmanager_widget_search.py
|
||||
addonmanager_widget_view_control_bar.py
|
||||
addonmanager_widget_view_selector.py
|
||||
|
||||
48
src/Mod/AddonManager/Widgets/addonmanager_colors.py
Normal file
48
src/Mod/AddonManager/Widgets/addonmanager_colors.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022-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 *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from enum import Enum, auto
|
||||
|
||||
import FreeCADGui
|
||||
from PySide import QtGui
|
||||
|
||||
|
||||
def is_darkmode() -> bool:
|
||||
"""Heuristics to determine if we are in a darkmode stylesheet"""
|
||||
pl = FreeCADGui.getMainWindow().palette()
|
||||
return pl.color(QtGui.QPalette.Window).lightness() < 128
|
||||
|
||||
|
||||
def warning_color_string() -> str:
|
||||
"""A shade of red, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio."""
|
||||
return "rgb(255,105,97)" if is_darkmode() else "rgb(215,0,21)"
|
||||
|
||||
|
||||
def bright_color_string() -> str:
|
||||
"""A shade of green, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio."""
|
||||
return "rgb(48,219,91)" if is_darkmode() else "rgb(36,138,61)"
|
||||
|
||||
|
||||
def attention_color_string() -> str:
|
||||
"""A shade of orange, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio."""
|
||||
return "rgb(255,179,64)" if is_darkmode() else "rgb(255,149,0)"
|
||||
@@ -77,39 +77,37 @@ class WidgetAddonButtons(QtWidgets.QWidget):
|
||||
def _setup_ui(self):
|
||||
self.horizontal_layout = QtWidgets.QHBoxLayout()
|
||||
self.horizontal_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.back_button = QtWidgets.QToolButton(self)
|
||||
self.install_button = QtWidgets.QPushButton(self)
|
||||
self.uninstall_button = QtWidgets.QPushButton(self)
|
||||
self.enable_button = QtWidgets.QPushButton(self)
|
||||
self.disable_button = QtWidgets.QPushButton(self)
|
||||
self.update_button = QtWidgets.QPushButton(self)
|
||||
self.run_macro_button = QtWidgets.QPushButton(self)
|
||||
self.change_branch_button = QtWidgets.QPushButton(self)
|
||||
self.check_for_update_button = QtWidgets.QPushButton(self)
|
||||
self.horizontal_layout.addWidget(self.back_button)
|
||||
self.back = QtWidgets.QToolButton(self)
|
||||
self.install = QtWidgets.QPushButton(self)
|
||||
self.uninstall = QtWidgets.QPushButton(self)
|
||||
self.enable = QtWidgets.QPushButton(self)
|
||||
self.disable = QtWidgets.QPushButton(self)
|
||||
self.update = QtWidgets.QPushButton(self)
|
||||
self.run_macro = QtWidgets.QPushButton(self)
|
||||
self.change_branch = QtWidgets.QPushButton(self)
|
||||
self.check_for_update = QtWidgets.QPushButton(self)
|
||||
self.horizontal_layout.addWidget(self.back)
|
||||
self.horizontal_layout.addStretch()
|
||||
self.horizontal_layout.addWidget(self.check_for_update_button)
|
||||
self.horizontal_layout.addWidget(self.install_button)
|
||||
self.horizontal_layout.addWidget(self.uninstall_button)
|
||||
self.horizontal_layout.addWidget(self.enable_button)
|
||||
self.horizontal_layout.addWidget(self.disable_button)
|
||||
self.horizontal_layout.addWidget(self.update_button)
|
||||
self.horizontal_layout.addWidget(self.run_macro_button)
|
||||
self.horizontal_layout.addWidget(self.change_branch_button)
|
||||
self.horizontal_layout.addWidget(self.check_for_update)
|
||||
self.horizontal_layout.addWidget(self.install)
|
||||
self.horizontal_layout.addWidget(self.uninstall)
|
||||
self.horizontal_layout.addWidget(self.enable)
|
||||
self.horizontal_layout.addWidget(self.disable)
|
||||
self.horizontal_layout.addWidget(self.update)
|
||||
self.horizontal_layout.addWidget(self.run_macro)
|
||||
self.horizontal_layout.addWidget(self.change_branch)
|
||||
self.setLayout(self.horizontal_layout)
|
||||
|
||||
def _set_icons(self):
|
||||
self.back_button.setIcon(
|
||||
QtGui.QIcon.fromTheme("back", QtGui.QIcon(":/icons/button_left.svg"))
|
||||
)
|
||||
self.back.setIcon(QtGui.QIcon.fromTheme("back", QtGui.QIcon(":/icons/button_left.svg")))
|
||||
|
||||
def retranslateUi(self, _):
|
||||
self.check_for_update_button.setText(translate("AddonsInstaller", "Check for update"))
|
||||
self.install_button.setText(translate("AddonsInstaller", "Install"))
|
||||
self.uninstall_button.setText(translate("AddonsInstaller", "Uninstall"))
|
||||
self.disable_button.setText(translate("AddonsInstaller", "Disable"))
|
||||
self.enable_button.setText(translate("AddonsInstaller", "Enable"))
|
||||
self.update_button.setText(translate("AddonsInstaller", "Update"))
|
||||
self.run_macro_button.setText(translate("AddonsInstaller", "Run"))
|
||||
self.change_branch_button.setText(translate("AddonsInstaller", "Change branch..."))
|
||||
self.back_button.setToolTip(translate("AddonsInstaller", "Return to package list"))
|
||||
self.check_for_update.setText(translate("AddonsInstaller", "Check for update"))
|
||||
self.install.setText(translate("AddonsInstaller", "Install"))
|
||||
self.uninstall.setText(translate("AddonsInstaller", "Uninstall"))
|
||||
self.disable.setText(translate("AddonsInstaller", "Disable"))
|
||||
self.enable.setText(translate("AddonsInstaller", "Enable"))
|
||||
self.update.setText(translate("AddonsInstaller", "Update"))
|
||||
self.run_macro.setText(translate("AddonsInstaller", "Run"))
|
||||
self.change_branch.setText(translate("AddonsInstaller", "Change branch..."))
|
||||
self.back.setToolTip(translate("AddonsInstaller", "Return to package list"))
|
||||
|
||||
@@ -109,5 +109,5 @@ class WidgetGlobalButtonBar(QtWidgets.QWidget):
|
||||
else:
|
||||
self.update_all_addons.setEnabled(True)
|
||||
self.update_all_addons.setText(
|
||||
translate("AddonsInstaller", "Apply %1 available updates").format(updates)
|
||||
translate("AddonsInstaller", "Apply {} available updates").format(updates)
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022-2024 FreeCAD Project Association *
|
||||
# * Copyright (c) 2022-2024 The FreeCAD Project Association AISBL *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
@@ -21,6 +21,10 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
try:
|
||||
import FreeCAD
|
||||
@@ -49,28 +53,286 @@ except ImportError:
|
||||
from PySide import QtCore, QtWidgets
|
||||
|
||||
from .addonmanager_widget_addon_buttons import WidgetAddonButtons
|
||||
from .addonmanager_widget_readme_browser import WidgetReadmeBrowser
|
||||
from .addonmanager_colors import warning_color_string, attention_color_string, bright_color_string
|
||||
|
||||
|
||||
class MessageType(Enum):
|
||||
Message = auto()
|
||||
Warning = auto()
|
||||
Error = auto()
|
||||
|
||||
|
||||
@dataclass
|
||||
class UpdateInformation:
|
||||
check_in_progress: bool = False
|
||||
update_available: bool = False
|
||||
detached_head: bool = False
|
||||
version: str = ""
|
||||
tag: str = ""
|
||||
branch: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class WarningFlags:
|
||||
obsolete: bool = False
|
||||
python2: bool = False
|
||||
required_freecad_version: Optional[str] = None
|
||||
non_osi_approved = False
|
||||
non_fsf_libre = False
|
||||
|
||||
|
||||
class PackageDetailsView(QtWidgets.QWidget):
|
||||
"""The view class for the package details"""
|
||||
|
||||
install_clicked = QtCore.Signal()
|
||||
uninstall_clicked = QtCore.Signal()
|
||||
enable_clicked = QtCore.Signal()
|
||||
disable_clicked = QtCore.Signal()
|
||||
update_clicked = QtCore.Signal()
|
||||
check_for_updates = QtCore.Signal()
|
||||
run_clicked = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent: QtWidgets.QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.button_bar = None
|
||||
self.text_browser = None
|
||||
self.readme_browser = None
|
||||
self.message_label = None
|
||||
self.location_label = None
|
||||
self.installed = False
|
||||
self.disabled = False
|
||||
self.update_info = UpdateInformation()
|
||||
self.warning_flags = WarningFlags()
|
||||
self.installed_version = None
|
||||
self.installed_branch = None
|
||||
self.installed_timestamp = None
|
||||
self.can_disable = True
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
self.vertical_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.button_bar = WidgetAddonButtons(self)
|
||||
self.text_browser = QtWidgets.QTextBrowser(self)
|
||||
self.readme_browser = WidgetReadmeBrowser(self)
|
||||
self.message_label = QtWidgets.QLabel(self)
|
||||
self.location_label = QtWidgets.QLabel(self)
|
||||
self.location_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
|
||||
self.vertical_layout.addWidget(self.button_bar)
|
||||
self.vertical_layout.addWidget(self.text_browser)
|
||||
self.vertical_layout.addWidget(self.message_label)
|
||||
self.vertical_layout.addWidget(self.location_label)
|
||||
self.vertical_layout.addWidget(self.readme_browser)
|
||||
|
||||
def set_location(self, location: Optional[str]):
|
||||
if location is not None:
|
||||
text = (
|
||||
translate("AddonsInstaller", "Installation location")
|
||||
+ ": "
|
||||
+ os.path.normpath(location)
|
||||
)
|
||||
self.location_label.setText(text)
|
||||
self.location_label.show()
|
||||
else:
|
||||
self.location_label.hide()
|
||||
|
||||
def set_installed(
|
||||
self,
|
||||
installed: bool,
|
||||
on_date: Optional[str] = None,
|
||||
version: Optional[str] = None,
|
||||
branch: Optional[str] = None,
|
||||
):
|
||||
self.installed = installed
|
||||
self.installed_timestamp = on_date
|
||||
self.installed_version = version
|
||||
self.installed_branch = branch
|
||||
if not self.installed:
|
||||
self.set_location(None)
|
||||
self._sync_ui_state()
|
||||
|
||||
def set_update_available(self, info: UpdateInformation):
|
||||
self.update_info = info
|
||||
self._sync_ui_state()
|
||||
|
||||
def set_disabled(self, disabled: bool):
|
||||
self.disabled = disabled
|
||||
self._sync_ui_state()
|
||||
|
||||
def allow_disabling(self, allow: bool):
|
||||
self.can_disable = allow
|
||||
self._sync_ui_state()
|
||||
|
||||
def allow_running(self, show: bool):
|
||||
self.button_bar.run_macro.setVisible(show)
|
||||
|
||||
def set_warning_flags(self, flags: WarningFlags):
|
||||
self.warning_flags = flags
|
||||
self._sync_ui_state()
|
||||
|
||||
def set_new_disabled_status(self, disabled: bool):
|
||||
"""If the user just changed the enabled/disabled state of the addon, display a message
|
||||
indicating that will not take place until restart. Do not call except in a case of a
|
||||
state change during this run."""
|
||||
|
||||
if disabled:
|
||||
message = translate(
|
||||
"AddonsInstaller", "This Addon will be disabled next time you restart FreeCAD."
|
||||
)
|
||||
else:
|
||||
message = translate(
|
||||
"AddonsInstaller", "This Addon will be enabled next time you restart FreeCAD."
|
||||
)
|
||||
self.message_label.setText(f"<h3>{message}</h3>")
|
||||
self.message_label.setStyleSheet("color:" + attention_color_string())
|
||||
|
||||
def set_new_branch(self, branch: str):
|
||||
"""If the user just changed branches, update the message to show that a restart is
|
||||
needed."""
|
||||
message_string = "<h3>"
|
||||
message_string += translate(
|
||||
"AddonsInstaller", "Changed to branch '{}' -- please restart to use Addon."
|
||||
).format(branch)
|
||||
message_string += "</h3>"
|
||||
self.message_label.setText(message_string)
|
||||
self.message_label.setStyleSheet("color:" + attention_color_string())
|
||||
|
||||
def set_updated(self):
|
||||
"""If the user has just updated the addon but not yet restarted, show an indication that
|
||||
we are awaiting a restart."""
|
||||
message = translate(
|
||||
"AddonsInstaller", "This Addon has been updated. Restart FreeCAD to see changes."
|
||||
)
|
||||
self.message_label.setText(f"<h3>{message}</h3>")
|
||||
self.message_label.setStyleSheet("color:" + attention_color_string())
|
||||
|
||||
def _sync_ui_state(self):
|
||||
self._sync_button_state()
|
||||
self._create_status_label_text()
|
||||
|
||||
def _sync_button_state(self):
|
||||
self.button_bar.install.setVisible(not self.installed)
|
||||
self.button_bar.uninstall.setVisible(self.installed)
|
||||
if not self.installed:
|
||||
self.button_bar.disable.hide()
|
||||
self.button_bar.enable.hide()
|
||||
self.button_bar.update.hide()
|
||||
self.button_bar.check_for_update.hide()
|
||||
else:
|
||||
self.button_bar.update.setVisible(self.update_info.update_available)
|
||||
if self.update_info.detached_head:
|
||||
self.button_bar.check_for_update.hide()
|
||||
else:
|
||||
self.button_bar.check_for_update.setVisible(not self.update_info.update_available)
|
||||
if self.can_disable:
|
||||
self.button_bar.enable.setVisible(self.disabled)
|
||||
self.button_bar.disable.setVisible(not self.disabled)
|
||||
else:
|
||||
self.button_bar.enable.hide()
|
||||
self.button_bar.disable.hide()
|
||||
|
||||
def _create_status_label_text(self):
|
||||
if self.installed:
|
||||
installation_details = self._get_installation_details_string()
|
||||
update_details = self._get_update_status_string()
|
||||
message_text = f"{installation_details} {update_details}"
|
||||
if self.disabled:
|
||||
message_text += " [" + translate("AddonsInstaller", "Disabled") + "]"
|
||||
self.message_label.setText(f"<h3>{message_text}</h3>")
|
||||
if self.disabled:
|
||||
self.message_label.setStyleSheet("color:" + warning_color_string())
|
||||
elif self.update_info.update_available:
|
||||
self.message_label.setStyleSheet("color:" + attention_color_string())
|
||||
else:
|
||||
self.message_label.setStyleSheet("color:" + bright_color_string())
|
||||
self.message_label.show()
|
||||
elif self._there_are_warnings_to_show():
|
||||
warnings = self._get_warning_string()
|
||||
self.message_label.setText(f"<h3>{warnings}</h3>")
|
||||
self.message_label.setStyleSheet("color:" + warning_color_string())
|
||||
self.message_label.show()
|
||||
else:
|
||||
self.message_label.hide()
|
||||
|
||||
def _get_installation_details_string(self) -> str:
|
||||
version = self.installed_version
|
||||
date = ""
|
||||
installed_version_string = ""
|
||||
if self.installed_timestamp:
|
||||
date = QtCore.QLocale().toString(
|
||||
QtCore.QDateTime.fromSecsSinceEpoch(int(round(self.installed_timestamp, 0))),
|
||||
QtCore.QLocale.ShortFormat,
|
||||
)
|
||||
if version and date:
|
||||
installed_version_string += (
|
||||
translate("AddonsInstaller", "Version {version} installed on {date}").format(
|
||||
version=version, date=date
|
||||
)
|
||||
+ ". "
|
||||
)
|
||||
elif version:
|
||||
installed_version_string += (
|
||||
translate("AddonsInstaller", "Version {version} installed") + "."
|
||||
).format(version=version)
|
||||
elif date:
|
||||
installed_version_string += (
|
||||
translate("AddonsInstaller", "Installed on {date}") + "."
|
||||
).format(date=date)
|
||||
else:
|
||||
installed_version_string += translate("AddonsInstaller", "Installed") + "."
|
||||
return installed_version_string
|
||||
|
||||
def _get_update_status_string(self) -> str:
|
||||
if self.update_info.check_in_progress:
|
||||
return translate("AddonsInstaller", "Update check in progress") + "."
|
||||
if self.update_info.detached_head:
|
||||
return (
|
||||
translate(
|
||||
"AddonsInstaller", "Git tag '{}' checked out, no updates possible"
|
||||
).format(self.update_info.tag)
|
||||
+ "."
|
||||
)
|
||||
if self.update_info.update_available:
|
||||
if self.installed_branch and self.update_info.branch:
|
||||
if self.installed_branch != self.update_info.branch:
|
||||
return (
|
||||
translate(
|
||||
"AddonsInstaller", "Currently on branch {}, name changed to {}"
|
||||
).format(self.installed_branch, self.update_info.branch)
|
||||
+ "."
|
||||
)
|
||||
if self.update_info.version:
|
||||
return (
|
||||
translate(
|
||||
"AddonsInstaller",
|
||||
"Currently on branch {}, update available to version {}",
|
||||
).format(self.installed_branch, str(self.update_info.version).strip())
|
||||
+ "."
|
||||
)
|
||||
return translate("AddonsInstaller", "Update available") + "."
|
||||
if self.update_info.version:
|
||||
return (
|
||||
translate("AddonsInstaller", "Update available to version {}").format(
|
||||
str(self.update_info.version).strip()
|
||||
)
|
||||
+ "."
|
||||
)
|
||||
return translate("AddonsInstaller", "Update available") + "."
|
||||
return translate("AddonsInstaller", "This is the latest version available") + "."
|
||||
|
||||
def _there_are_warnings_to_show(self) -> bool:
|
||||
if self.disabled:
|
||||
return True
|
||||
if (
|
||||
self.warning_flags.obsolete
|
||||
or self.warning_flags.python2
|
||||
or self.warning_flags.required_freecad_version
|
||||
):
|
||||
return True
|
||||
return False # TODO: Someday support optional warnings on license types
|
||||
|
||||
def _get_warning_string(self) -> str:
|
||||
if self.installed and self.disabled:
|
||||
return translate(
|
||||
"AddonsInstaller",
|
||||
"WARNING: This addon is currently installed, but disabled. Use the 'enable' "
|
||||
"button to re-enable.",
|
||||
)
|
||||
if self.warning_flags.obsolete:
|
||||
return translate("AddonsInstaller", "WARNING: This addon is obsolete")
|
||||
if self.warning_flags.python2:
|
||||
return translate("AddonsInstaller", "WARNING: This addon is Python 2 only")
|
||||
if self.warning_flags.required_freecad_version:
|
||||
return translate("AddonsInstaller", "WARNING: This addon requires FreeCAD {}").format(
|
||||
self.warning_flags.required_freecad_version
|
||||
)
|
||||
return ""
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022-2024 The FreeCAD Project Association AISBL *
|
||||
# * *
|
||||
# * 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 FreeCAD
|
||||
|
||||
# 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 QtCore, QtGui, QtWidgets
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class WidgetReadmeBrowser(QtWidgets.QTextBrowser):
|
||||
"""A QTextBrowser widget that emits signals for each requested image resource, allowing an external controller
|
||||
to load and re-deliver those images. Once all resources have been re-delivered, the original data is redisplayed
|
||||
with the images in-line. Call setUrl prior to calling setMarkdown or setHtml to ensure URLs are resolved
|
||||
correctly."""
|
||||
|
||||
load_resource = QtCore.Signal(str) # Str is a URL to a resource
|
||||
|
||||
def __init__(self, parent: QtWidgets.QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.image_map = {}
|
||||
self.url = ""
|
||||
self.stop = False
|
||||
self.setOpenExternalLinks(True)
|
||||
|
||||
def setUrl(self, url: str):
|
||||
"""Set the base URL of the page. Used to resolve relative URLs in the page source."""
|
||||
self.url = url
|
||||
|
||||
def setMarkdown(self, md: str):
|
||||
"""Provides an optional fallback to the markdown library for older versions of Qt (prior to 5.15) that did not
|
||||
have native markdown support. Lacking that, plaintext is displayed."""
|
||||
if hasattr(super(), "setMarkdown"):
|
||||
super().setMarkdown(md)
|
||||
else:
|
||||
try:
|
||||
import markdown
|
||||
|
||||
html = markdown.markdown(md)
|
||||
self.setHtml(html)
|
||||
except ImportError:
|
||||
self.setText(md)
|
||||
FreeCAD.Console.Warning(
|
||||
"Qt < 5.15 and no `import markdown` -- falling back to plain text display\n"
|
||||
)
|
||||
|
||||
def set_resource(self, resource_url: str, image: Optional[QtGui.QImage]):
|
||||
"""Once a resource has been fetched (or the fetch has failed), this method should be used to inform the widget
|
||||
that the resource has been loaded. Note that the incoming image is scaled to 97% of the widget width if it is
|
||||
larger than that."""
|
||||
self.image_map[resource_url] = self._ensure_appropriate_width(image)
|
||||
|
||||
def loadResource(self, resource_type: int, name: QtCore.QUrl) -> object:
|
||||
"""Callback for resource loading. Called automatically by underlying Qt
|
||||
code when external resources are needed for rendering. In particular,
|
||||
here it is used to download and cache (in RAM) the images needed for the
|
||||
README and Wiki pages."""
|
||||
if resource_type == QtGui.QTextDocument.ImageResource and not self.stop:
|
||||
full_url = self._create_full_url(name.toString())
|
||||
if full_url not in self.image_map:
|
||||
self.load_resource.emit(full_url)
|
||||
self.image_map[full_url] = None
|
||||
return self.image_map[full_url]
|
||||
return super().loadResource(resource_type, name)
|
||||
|
||||
def _ensure_appropriate_width(self, image: QtGui.QImage) -> QtGui.QImage:
|
||||
ninety_seven_percent = self.width() * 0.97
|
||||
if image.width() < ninety_seven_percent:
|
||||
return image
|
||||
return image.scaledToWidth(ninety_seven_percent)
|
||||
|
||||
def _create_full_url(self, url: str) -> str:
|
||||
if url.startswith("http"):
|
||||
return url
|
||||
if not self.url:
|
||||
return url
|
||||
lhs, slash, _ = self.url.rpartition("/")
|
||||
return lhs + slash + url
|
||||
@@ -119,6 +119,7 @@ class WidgetViewSelector(QtWidgets.QWidget):
|
||||
self.composite_button.setIcon(
|
||||
QtGui.QIcon.fromTheme("composite_button", QtGui.QIcon(":/icons/composite_view.svg"))
|
||||
)
|
||||
self.composite_button.hide() # TODO: Implement this view
|
||||
|
||||
self.horizontal_layout.addWidget(self.compact_button)
|
||||
self.horizontal_layout.addWidget(self.expanded_button)
|
||||
|
||||
Reference in New Issue
Block a user