# SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * * # * Copyright (c) 2022-2023 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 the PackageList QWidget for displaying a list of Addons. """ import datetime import threading import FreeCAD from PySide import QtCore, QtGui, QtWidgets from Addon import Addon from compact_view import Ui_CompactView from expanded_view import Ui_ExpandedView import addonmanager_utilities as utils from addonmanager_metadata import get_first_supported_freecad_version, Version from Widgets.addonmanager_widget_view_control_bar import WidgetViewControlBar, SortOptions from Widgets.addonmanager_widget_view_selector import AddonManagerDisplayStyle from Widgets.addonmanager_widget_filter_selector import StatusFilter, Filter from Widgets.addonmanager_widget_progress_bar import WidgetProgressBar from addonmanager_licenses import get_license_manager translate = FreeCAD.Qt.translate # pylint: disable=too-few-public-methods class PackageList(QtWidgets.QWidget): """A widget that shows a list of packages and various widgets to control the display of the list""" itemSelected = QtCore.Signal(Addon) def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_PackageList() self.ui.setupUi(self) self.item_filter = PackageListFilter() self.ui.listPackages.setModel(self.item_filter) self.item_delegate = PackageListItemDelegate(self.ui.listPackages) self.ui.listPackages.setItemDelegate(self.item_delegate) self.ui.listPackages.clicked.connect(self.on_listPackages_clicked) self.ui.view_bar.filter_changed.connect(self.update_status_filter) self.ui.view_bar.search_changed.connect(self.item_filter.setFilterRegularExpression) self.ui.view_bar.sort_changed.connect(self.item_filter.setSortRole) self.ui.view_bar.sort_changed.connect(self.item_delegate.set_sort) self.ui.view_bar.sort_order_changed.connect(lambda order: self.item_filter.sort(0, order)) # Set up the view the same as the last time: pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") package_type = pref.GetInt("PackageTypeSelection", 0) status = pref.GetInt("StatusSelection", 0) search_string = pref.GetString("SearchString", "") self.ui.view_bar.filter_selector.set_contents_filter(package_type) self.ui.view_bar.filter_selector.set_status_filter(status) if search_string: self.ui.view_bar.search.filter_line_edit.setText(search_string) self.item_filter.setPackageFilter(package_type) self.item_filter.setStatusFilter(status) # Pre-init of other members: self.item_model = None def setModel(self, model): """This is a model-view-controller widget: set its model.""" self.item_model = model self.item_filter.setSourceModel(self.item_model) self.item_filter.setSortRole(SortOptions.Alphabetical) self.item_filter.sort(0, QtCore.Qt.AscendingOrder) pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") style = pref.GetInt("ViewStyle", AddonManagerDisplayStyle.EXPANDED) self.set_view_style(style) self.ui.view_bar.view_selector.set_current_view(style) self.item_filter.setHidePy2(pref.GetBool("HidePy2", False)) self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", False)) self.item_filter.setHideNonOSIApproved(pref.GetBool("HideNonOSIApproved", False)) self.item_filter.setHideNonFSFLibre(pref.GetBool("HideNonFSFFreeLibre", False)) self.item_filter.setHideNewerFreeCADRequired( pref.GetBool("HideNewerFreeCADRequired", False) ) self.item_filter.setHideUnlicensed(pref.GetBool("HideUnlicensed", False)) def select_addon(self, addon_name: str): for index, addon in enumerate(self.item_model.repos): if addon.name == addon_name: row_index = self.item_model.createIndex(index, 0) if self.item_filter.filterAcceptsRow(index): self.ui.listPackages.setCurrentIndex(row_index) else: FreeCAD.Console.PrintLog( f"Addon {addon_name} is not visible given current " "filter: not selecting it." ) return FreeCAD.Console.PrintLog(f"Could not find addon '{addon_name}' to select it") def on_listPackages_clicked(self, index: QtCore.QModelIndex): """Determine what addon was selected and emit the itemSelected signal with it as an argument.""" source_selection = self.item_filter.mapToSource(index) selected_repo = self.item_model.repos[source_selection.row()] self.itemSelected.emit(selected_repo) def update_status_filter(self, new_filter: Filter) -> None: """hide/show rows corresponding to the specified filter""" self.item_filter.setStatusFilter(new_filter.status_filter) self.item_filter.setPackageFilter(new_filter.content_filter) pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") pref.SetInt("StatusSelection", new_filter.status_filter) pref.SetInt("PackageTypeSelection", new_filter.content_filter) self.item_filter.invalidateFilter() def set_view_style(self, style: AddonManagerDisplayStyle) -> None: """Set the style (compact or expanded) of the list""" if self.item_model: self.item_model.layoutAboutToBeChanged.emit() self.item_delegate.set_view(style) if style == AddonManagerDisplayStyle.COMPACT or style == AddonManagerDisplayStyle.COMPOSITE: self.ui.listPackages.setSpacing(2) self.ui.listPackages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) self.ui.listPackages.verticalScrollBar().setSingleStep(-1) else: self.ui.listPackages.setSpacing(5) self.ui.listPackages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.ui.listPackages.verticalScrollBar().setSingleStep(24) if self.item_model: self.item_model.layoutChanged.emit() pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") pref.SetInt("ViewStyle", style) class PackageListItemModel(QtCore.QAbstractListModel): """The model for use with the PackageList class.""" repos = [] write_lock = threading.Lock() DataAccessRole = QtCore.Qt.UserRole def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int: """The number of rows""" if parent.isValid(): return 0 return len(self.repos) def columnCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int: """Only one column, always returns 1.""" if parent.isValid(): return 0 return 1 def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.DisplayRole): """Get the data for a given index and role.""" if not index.isValid(): return None row = index.row() if role == QtCore.Qt.ToolTipRole: tooltip = "" if self.repos[row].repo_type == Addon.Kind.PACKAGE: tooltip = translate("AddonsInstaller", "Click for details about package {}").format( self.repos[row].display_name ) elif self.repos[row].repo_type == Addon.Kind.WORKBENCH: tooltip = translate( "AddonsInstaller", "Click for details about workbench {}" ).format(self.repos[row].display_name) elif self.repos[row].repo_type == Addon.Kind.MACRO: tooltip = translate("AddonsInstaller", "Click for details about macro {}").format( self.repos[row].display_name ) return tooltip if role == PackageListItemModel.DataAccessRole: return self.repos[row] # Sorting if role == SortOptions.Alphabetical: return self.repos[row].display_name if role == SortOptions.LastUpdated: update_date = self.repos[row].update_date if update_date and hasattr(update_date, "timestamp"): return update_date.timestamp() return 0 if role == SortOptions.DateAdded: if self.repos[row].stats and self.repos[row].stats.date_created: return self.repos[row].stats.date_created.timestamp() return 0 if role == SortOptions.Stars: if self.repos[row].stats and self.repos[row].stats.stars: return self.repos[row].stats.stars return 0 if role == SortOptions.Score: return self.repos[row].score def headerData(self, _unused1, _unused2, _role=QtCore.Qt.DisplayRole): """No header in this implementation: always returns None.""" return None def append_item(self, repo: Addon) -> None: """Adds this addon to the end of the model. Thread safe.""" if repo in self.repos: # Cowardly refuse to insert the same repo a second time return with self.write_lock: self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount()) self.repos.append(repo) self.endInsertRows() def clear(self) -> None: """Clear the model, removing all rows. Thread safe.""" if self.rowCount() > 0: with self.write_lock: self.beginRemoveRows(QtCore.QModelIndex(), 0, self.rowCount() - 1) self.repos = [] self.endRemoveRows() def reload_item(self, repo: Addon) -> None: """Sets the addon data for the given addon (based on its name)""" for index, item in enumerate(self.repos): if item.name == repo.name: with self.write_lock: self.repos[index] = repo return class CompactView(QtWidgets.QWidget): """A single-line view of the package information""" def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_CompactView() self.ui.setupUi(self) class ExpandedView(QtWidgets.QWidget): """A multi-line view of the package information""" def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_ExpandedView() self.ui.setupUi(self) class PackageListItemDelegate(QtWidgets.QStyledItemDelegate): """Render the repo data as a formatted region""" def __init__(self, parent=None): super().__init__(parent) self.displayStyle = AddonManagerDisplayStyle.EXPANDED self.sort_order = SortOptions.Alphabetical self.expanded = ExpandedView() self.compact = CompactView() self.widget = self.expanded def set_view(self, style: AddonManagerDisplayStyle) -> None: """Set the view of to style""" if not self.displayStyle == style: self.displayStyle = style def set_sort(self, sort: SortOptions) -> None: """When sorting by various things, we display the thing that's being sorted on.""" if not self.sort_order == sort: self.sort_order = sort def sizeHint(self, _option, index): """Attempt to figure out the correct height for the widget based on its current contents.""" self.update_content(index) return self.widget.sizeHint() def update_content(self, index): """Creates the display of the content for a given index.""" repo = index.data(PackageListItemModel.DataAccessRole) if self.displayStyle == AddonManagerDisplayStyle.EXPANDED: self.widget = self.expanded self._setup_expanded_view(repo) elif self.displayStyle == AddonManagerDisplayStyle.COMPACT: self.widget = self.compact self._setup_compact_view(repo) elif self.displayStyle == AddonManagerDisplayStyle.COMPOSITE: self.widget = self.compact # For now re-use the compact list self._setup_composite_view(repo) self.widget.adjustSize() def _setup_expanded_view(self, addon: Addon) -> None: self.widget.ui.labelPackageName.setText(f"

{addon.display_name}

") self.widget.ui.labelIcon.setPixmap(addon.icon.pixmap(QtCore.QSize(48, 48))) self.widget.ui.labelStatus.setText(self.get_expanded_update_string(addon)) self.widget.ui.labelIcon.setText("") self.widget.ui.labelTags.setText("") if addon.metadata: self.widget.ui.labelDescription.setText(addon.metadata.description) self.widget.ui.labelVersion.setText(f"v{addon.metadata.version}") self._set_package_maintainer_label(addon) elif addon.macro: self.widget.ui.labelDescription.setText(addon.macro.comment) self._set_macro_version_label(addon) self._set_macro_maintainer_label(addon) else: self.widget.ui.labelDescription.setText("") self.widget.ui.labelMaintainer.setText("") self.widget.ui.labelVersion.setText("") if addon.tags: self.widget.ui.labelTags.setText( translate("AddonsInstaller", "Tags") + ": " + ", ".join(addon.tags) ) if self.sort_order == SortOptions.Alphabetical: self.widget.ui.labelSort.setText("") else: self.widget.ui.labelSort.setText(self._get_sort_label_text(addon)) def _setup_compact_view(self, addon: Addon) -> None: self.widget.ui.labelPackageName.setText(f"{addon.display_name}") self.widget.ui.labelIcon.setPixmap(addon.icon.pixmap(QtCore.QSize(16, 16))) self.widget.ui.labelStatus.setText(self.get_compact_update_string(addon)) self.widget.ui.labelIcon.setText("") if addon.metadata: self.widget.ui.labelVersion.setText(f"v{addon.metadata.version}") elif addon.macro: self._set_macro_version_label(addon) else: self.widget.ui.labelVersion.setText("") if self.sort_order == SortOptions.Alphabetical: description = self._get_compact_description(addon) self.widget.ui.labelDescription.setText(description) else: self.widget.ui.labelDescription.setText(self._get_sort_label_text(addon)) def _setup_composite_view(self, addon: Addon) -> None: self.widget.ui.labelPackageName.setText(f"{addon.display_name}") self.widget.ui.labelIcon.setPixmap(addon.icon.pixmap(QtCore.QSize(16, 16))) self.widget.ui.labelStatus.setText(self.get_compact_update_string(addon)) self.widget.ui.labelIcon.setText("") if addon.metadata: self.widget.ui.labelVersion.setText(f"v{addon.metadata.version}") elif addon.macro: self._set_macro_version_label(addon) else: self.widget.ui.labelVersion.setText("") if self.sort_order != SortOptions.Alphabetical: self.widget.ui.labelDescription.setText(self._get_sort_label_text(addon)) else: self.widget.ui.labelDescription.setText("") def _set_package_maintainer_label(self, addon: Addon): maintainers = addon.metadata.maintainer maintainers_string = "" if len(maintainers) == 1: maintainers_string = ( translate("AddonsInstaller", "Maintainer") + f": {maintainers[0].name} <{maintainers[0].email}>" ) elif len(maintainers) > 1: n = len(maintainers) maintainers_string = translate("AddonsInstaller", "Maintainers:", "", n) for maintainer in maintainers: maintainers_string += f"\n{maintainer.name} <{maintainer.email}>" self.widget.ui.labelMaintainer.setText(maintainers_string) def _set_macro_maintainer_label(self, repo: Addon): if repo.macro.author: caption = translate("AddonsInstaller", "Author") self.widget.ui.labelMaintainer.setText(caption + ": " + repo.macro.author) else: self.widget.ui.labelMaintainer.setText("") def _set_macro_version_label(self, addon: Addon): version_string = "" if addon.macro.version: version_string = addon.macro.version + " " if addon.macro.on_wiki: version_string += "(wiki)" elif addon.macro.on_git: version_string += "(git)" else: version_string += "(unknown source)" self.widget.ui.labelVersion.setText("" + version_string + "") def _get_sort_label_text(self, addon: Addon) -> str: if self.sort_order == SortOptions.Alphabetical: return "" elif self.sort_order == SortOptions.Stars: if addon.stats and addon.stats.stars and addon.stats.stars > 0: return translate("AddonsInstaller", "{} ★ on GitHub").format(addon.stats.stars) return translate("AddonsInstaller", "No ★, or not on GitHub") elif self.sort_order == SortOptions.DateAdded: if addon.stats and addon.stats.date_created: epoch_seconds = addon.stats.date_created.timestamp() qdt = QtCore.QDateTime.fromSecsSinceEpoch(int(epoch_seconds)).date() time_string = QtCore.QLocale().toString(qdt, QtCore.QLocale.ShortFormat) return translate("AddonsInstaller", "Created ") + time_string return "" elif self.sort_order == SortOptions.LastUpdated: update_date = addon.update_date if update_date: epoch_seconds = update_date.timestamp() qdt = QtCore.QDateTime.fromSecsSinceEpoch(int(epoch_seconds)).date() time_string = QtCore.QLocale().toString(qdt, QtCore.QLocale.ShortFormat) return translate("AddonsInstaller", "Updated ") + time_string return "" elif self.sort_order == SortOptions.Score: return translate("AddonsInstaller", "Score: ") + str(addon.score) return "" def _get_compact_description(self, addon: Addon) -> str: description = "" if addon.metadata: description = addon.metadata.description elif addon.macro and addon.macro.comment: description = addon.macro.comment trimmed_text, _, _ = description.partition(".") return trimmed_text.replace("\n", " ") @staticmethod def get_compact_update_string(repo: Addon) -> str: """Get a single-line string listing details about the installed version and date""" result = "" if repo.status() == Addon.Status.UNCHECKED: result = translate("AddonsInstaller", "Installed") elif repo.status() == Addon.Status.NO_UPDATE_AVAILABLE: result = translate("AddonsInstaller", "Up-to-date") elif repo.status() == Addon.Status.UPDATE_AVAILABLE: result = translate("AddonsInstaller", "Update available") elif repo.status() == Addon.Status.PENDING_RESTART: result = translate("AddonsInstaller", "Pending restart") if repo.is_disabled(): style = "style='color:" + utils.warning_color_string() + "; font-weight:bold;'" result += f" [" + translate("AddonsInstaller", "DISABLED") + "]" return result @staticmethod def get_expanded_update_string(repo: Addon) -> str: """Get a multi-line string listing details about the installed version and date""" result = "" installed_version_string = "" if repo.status() != Addon.Status.NOT_INSTALLED: if repo.installed_version or repo.installed_metadata: installed_version_string = ( "
" + translate("AddonsInstaller", "Installed version") + ": " ) if repo.installed_metadata: installed_version_string += str(repo.installed_metadata.version) elif repo.installed_version: installed_version_string += str(repo.installed_version) else: installed_version_string = "
" + translate("AddonsInstaller", "Unknown version") installed_date_string = "" if repo.updated_timestamp: installed_date_string = "
" + translate("AddonsInstaller", "Installed on") + ": " installed_date_string += QtCore.QLocale().toString( QtCore.QDateTime.fromSecsSinceEpoch(int(round(repo.updated_timestamp, 0))), QtCore.QLocale.ShortFormat, ) available_version_string = "" if repo.metadata: available_version_string = ( "
" + translate("AddonsInstaller", "Available version") + ": " ) available_version_string += str(repo.metadata.version) if repo.status() == Addon.Status.UNCHECKED: result = translate("AddonsInstaller", "Installed") result += installed_version_string result += installed_date_string elif repo.status() == Addon.Status.NO_UPDATE_AVAILABLE: result = translate("AddonsInstaller", "Up-to-date") result += installed_version_string result += installed_date_string elif repo.status() == Addon.Status.UPDATE_AVAILABLE: result = translate("AddonsInstaller", "Update available") result += installed_version_string result += installed_date_string result += available_version_string elif repo.status() == Addon.Status.PENDING_RESTART: result = translate("AddonsInstaller", "Pending restart") if repo.is_disabled(): style = "style='color:" + utils.warning_color_string() + "; font-weight:bold;'" result += ( f"
[" + translate("AddonsInstaller", "DISABLED") + "]" ) return result def paint( self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem, _: QtCore.QModelIndex, ): """Main paint function: renders this widget into a given rectangle, successively drawing all of its children.""" painter.save() self.widget.resize(option.rect.size()) painter.translate(option.rect.topLeft()) self.widget.render( painter, QtCore.QPoint(), QtGui.QRegion(), QtWidgets.QWidget.DrawChildren ) painter.restore() class PackageListFilter(QtCore.QSortFilterProxyModel): """Handle filtering the item list on various criteria""" def __init__(self): super().__init__() self.package_type = 0 # Default to showing everything self.status = 0 # Default to showing any self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) self.hide_obsolete = False self.hide_py2 = False self.hide_non_OSI_approved = False self.hide_non_FSF_libre = False self.hide_unlicensed = False self.hide_newer_freecad_required = False def setPackageFilter( self, package_type: int ) -> None: # 0=All, 1=Workbenches, 2=Macros, 3=Preference Packs """Set the package filter to package_type and refreshes.""" self.package_type = package_type self.invalidateFilter() def setStatusFilter( self, status: int ) -> None: # 0=Any, 1=Installed, 2=Not installed, 3=Update available """Sets the status filter to status and refreshes.""" self.status = status self.invalidateFilter() def setHidePy2(self, hide_py2: bool) -> None: """Sets whether to hide Python 2-only Addons""" self.hide_py2 = hide_py2 self.invalidateFilter() def setHideObsolete(self, hide_obsolete: bool) -> None: """Sets whether to hide Addons marked obsolete""" self.hide_obsolete = hide_obsolete self.invalidateFilter() def setHideNonOSIApproved(self, hide: bool) -> None: """Sets whether to hide Addons with non-OSI-approved licenses""" self.hide_non_OSI_approved = hide self.invalidateFilter() def setHideNonFSFLibre(self, hide: bool) -> None: """Sets whether to hide Addons with non-FSF-Libre licenses""" self.hide_non_FSF_libre = hide self.invalidateFilter() def setHideUnlicensed(self, hide: bool) -> None: """Sets whether to hide addons without a specified license""" self.hide_unlicensed = hide self.invalidateFilter() def setHideNewerFreeCADRequired(self, hide_nfr: bool) -> None: """Sets whether to hide packages that have indicated they need a newer version of FreeCAD than the one currently running.""" self.hide_newer_freecad_required = hide_nfr self.invalidateFilter() # def lessThan(self, left_in, right_in) -> bool: # """Enable sorting of display name (not case-sensitive).""" # # left = self.sourceModel().data(left_in, self.sortRole) # right = self.sourceModel().data(right_in, self.sortRole) # # return left.display_name.lower() < right.display_name.lower() def filterAcceptsRow(self, row, _parent=QtCore.QModelIndex()): """Do the actual filtering (called automatically by Qt when drawing the list)""" index = self.sourceModel().createIndex(row, 0) data = self.sourceModel().data(index, PackageListItemModel.DataAccessRole) if self.package_type == 1: if not data.contains_workbench(): return False elif self.package_type == 2: if not data.contains_macro(): return False elif self.package_type == 3: if not data.contains_preference_pack(): return False if self.status == StatusFilter.INSTALLED: if data.status() == Addon.Status.NOT_INSTALLED: return False elif self.status == StatusFilter.NOT_INSTALLED: if data.status() != Addon.Status.NOT_INSTALLED: return False elif self.status == StatusFilter.UPDATE_AVAILABLE: if data.status() != Addon.Status.UPDATE_AVAILABLE: return False license_manager = get_license_manager() if data.status() == Addon.Status.NOT_INSTALLED: # If it's not installed, check to see if it's Py2 only if self.hide_py2 and data.python2: return False # If it's not installed, check to see if it's marked obsolete if self.hide_obsolete and data.obsolete: return False if self.hide_unlicensed: if not data.license or data.license in ["UNLICENSED", "UNLICENCED"]: FreeCAD.Console.PrintLog(f"Hiding {data.name} because it has no license set\n") return False # If it is not an OSI-approved license, check to see if we are hiding those if self.hide_non_OSI_approved or self.hide_non_FSF_libre: if not data.license: return False licenses_to_check = [] if type(data.license) is str: licenses_to_check.append(data.license) elif type(data.license) is list: for license_id in data.license: if type(license_id) is str: licenses_to_check.append(license_id) else: licenses_to_check.append(license_id.name) else: licenses_to_check.append(data.license.name) fsf_libre = False osi_approved = False for license_id in licenses_to_check: if not osi_approved and license_manager.is_osi_approved(license_id): osi_approved = True if not fsf_libre and license_manager.is_fsf_libre(license_id): fsf_libre = True if self.hide_non_OSI_approved and not osi_approved: # FreeCAD.Console.PrintLog( # f"Hiding addon {data.name} because its license, {licenses_to_check}, " # f"is " # f"not OSI approved\n" # ) return False if self.hide_non_FSF_libre and not fsf_libre: # FreeCAD.Console.PrintLog( # f"Hiding addon {data.name} because its license, {licenses_to_check}, is " # f"not FSF Libre\n" # ) return False # If it's not installed, check to see if it's for a newer version of FreeCAD if ( data.status() == Addon.Status.NOT_INSTALLED and self.hide_newer_freecad_required and data.metadata ): # Only hide if ALL content items require a newer version, otherwise # it's possible that this package actually provides versions of itself # for newer and older versions first_supported_version = get_first_supported_freecad_version(data.metadata) if first_supported_version is not None: current_fc_version = Version(from_list=FreeCAD.Version()) if first_supported_version > current_fc_version: return False name = data.display_name desc = data.description if hasattr(self, "filterRegularExpression"): # Added in Qt 5.12 re = self.filterRegularExpression() if re.isValid(): re.setPatternOptions(QtCore.QRegularExpression.CaseInsensitiveOption) if re.match(name).hasMatch(): return True if re.match(desc).hasMatch(): return True if data.macro and data.macro.comment and re.match(data.macro.comment).hasMatch(): return True for tag in data.tags: if re.match(tag).hasMatch(): return True return False # Only get here for Qt < 5.12 re = self.filterRegExp() if re.isValid(): re.setCaseSensitivity(QtCore.Qt.CaseInsensitive) if re.indexIn(name) != -1: return True if re.indexIn(desc) != -1: return True if data.macro and data.macro.comment and re.indexIn(data.macro.comment) != -1: return True for tag in data.tags: if re.indexIn(tag) != -1: return True return False # pylint: disable=attribute-defined-outside-init, missing-function-docstring class Ui_PackageList: """The contents of the PackageList widget""" def setupUi(self, form): if not form.objectName(): form.setObjectName("PackageList") self.verticalLayout = QtWidgets.QVBoxLayout(form) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout_6 = QtWidgets.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.view_bar = WidgetViewControlBar(form) self.view_bar.setObjectName("ViewControlBar") self.horizontalLayout_6.addWidget(self.view_bar) self.verticalLayout.addLayout(self.horizontalLayout_6) self.listPackages = QtWidgets.QListView(form) self.listPackages.setObjectName("listPackages") self.listPackages.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.listPackages.setProperty("showDropIndicator", False) self.listPackages.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.listPackages.setResizeMode(QtWidgets.QListView.Adjust) self.listPackages.setUniformItemSizes(False) self.listPackages.setAlternatingRowColors(True) self.listPackages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.listPackages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.listPackages.verticalScrollBar().setSingleStep(24) self.verticalLayout.addWidget(self.listPackages) self.progressBar = WidgetProgressBar() self.verticalLayout.addWidget(self.progressBar) QtCore.QMetaObject.connectSlotsByName(form)