Files
create/src/Mod/AddonManager/package_list.py
2022-02-27 22:50:54 -06:00

788 lines
30 KiB
Python

# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program 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 Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import FreeCAD
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from enum import IntEnum
import threading
from Addon import Addon
from compact_view import Ui_CompactView
from expanded_view import Ui_ExpandedView
import addonmanager_utilities as utils
translate = FreeCAD.Qt.translate
class ListDisplayStyle(IntEnum):
COMPACT = 0
EXPANDED = 1
class StatusFilter(IntEnum):
ANY = 0
INSTALLED = 1
NOT_INSTALLED = 2
UPDATE_AVAILABLE = 3
class PackageList(QWidget):
"""A widget that shows a list of packages and various widgets to control the display of the list"""
itemSelected = 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.comboPackageType.currentIndexChanged.connect(self.update_type_filter)
self.ui.comboStatus.currentIndexChanged.connect(self.update_status_filter)
self.ui.lineEditFilter.textChanged.connect(self.update_text_filter)
self.ui.buttonCompactLayout.clicked.connect(
lambda: self.set_view_style(ListDisplayStyle.COMPACT)
)
self.ui.buttonExpandedLayout.clicked.connect(
lambda: self.set_view_style(ListDisplayStyle.EXPANDED)
)
# Only shows when the user types in a filter
self.ui.labelFilterValidity.hide()
# Set up the view the same as the last time:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
package_type = pref.GetInt("PackageTypeSelection", 1)
self.ui.comboPackageType.setCurrentIndex(package_type)
status = pref.GetInt("StatusSelection", 0)
self.ui.comboStatus.setCurrentIndex(status)
def setModel(self, model):
self.item_model = model
self.item_filter.setSourceModel(self.item_model)
self.item_filter.sort(0)
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
style = pref.GetInt("ViewStyle", ListDisplayStyle.EXPANDED)
self.set_view_style(style)
if style == ListDisplayStyle.EXPANDED:
self.ui.buttonExpandedLayout.setChecked(True)
else:
self.ui.buttonCompactLayout.setChecked(True)
self.item_filter.setHidePy2(pref.GetBool("HidePy2", True))
self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", True))
self.item_filter.setHideNewerFreeCADRequired(
pref.GetBool("HideNewerFreeCADRequired", True)
)
def on_listPackages_clicked(self, index: QModelIndex):
source_selection = self.item_filter.mapToSource(index)
selected_repo = self.item_model.repos[source_selection.row()]
self.itemSelected.emit(selected_repo)
def update_type_filter(self, type_filter: int) -> None:
"""hide/show rows corresponding to the type filter
type_filter is an integer: 0 for all, 1 for workbenches, 2 for macros, and 3 for preference packs
"""
self.item_filter.setPackageFilter(type_filter)
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
pref.SetInt("PackageTypeSelection", type_filter)
def update_status_filter(self, status_filter: int) -> None:
"""hide/show rows corresponding to the status filter
status_filter is an integer: 0 for any, 1 for installed, 2 for not installed, and 3 for update available
"""
self.item_filter.setStatusFilter(status_filter)
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
pref.SetInt("StatusSelection", status_filter)
def update_text_filter(self, text_filter: str) -> None:
"""filter name and description by the regex specified by text_filter"""
if text_filter:
if hasattr(
self.item_filter, "setFilterRegularExpression"
): # Added in Qt 5.12
test_regex = QRegularExpression(text_filter)
else:
test_regex = QRegExp(text_filter)
if test_regex.isValid():
self.ui.labelFilterValidity.setToolTip(
translate("AddonsInstaller", "Filter is valid")
)
icon = QIcon.fromTheme("ok", QIcon(":/icons/edit_OK.svg"))
self.ui.labelFilterValidity.setPixmap(icon.pixmap(16, 16))
else:
self.ui.labelFilterValidity.setToolTip(
translate("AddonsInstaller", "Filter regular expression is invalid")
)
icon = QIcon.fromTheme("cancel", QIcon(":/icons/edit_Cancel.svg"))
self.ui.labelFilterValidity.setPixmap(icon.pixmap(16, 16))
self.ui.labelFilterValidity.show()
else:
self.ui.labelFilterValidity.hide()
if hasattr(self.item_filter, "setFilterRegularExpression"): # Added in Qt 5.12
self.item_filter.setFilterRegularExpression(text_filter)
else:
self.item_filter.setFilterRegExp(text_filter)
def set_view_style(self, style: ListDisplayStyle) -> None:
self.item_model.layoutAboutToBeChanged.emit()
self.item_delegate.set_view(style)
if style == ListDisplayStyle.COMPACT:
self.ui.listPackages.setSpacing(2)
else:
self.ui.listPackages.setSpacing(5)
self.item_model.layoutChanged.emit()
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
pref.SetInt("ViewStyle", style)
class PackageListItemModel(QAbstractListModel):
repos = []
write_lock = threading.Lock()
DataAccessRole = Qt.UserRole
StatusUpdateRole = Qt.UserRole + 1
IconUpdateRole = Qt.UserRole + 2
def __init__(self, parent=None) -> None:
super().__init__(parent)
def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
if parent.isValid():
return 0
return len(self.repos)
def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
if parent.isValid():
return 0
return 1
def data(self, index: QModelIndex, role: int = Qt.DisplayRole):
if not index.isValid():
return None
row = index.row()
if role == 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
elif role == PackageListItemModel.DataAccessRole:
return self.repos[row]
def headerData(self, section, orientation, role=Qt.DisplayRole):
return None
def setData(self, index: QModelIndex, value, role=Qt.EditRole) -> None:
"""Set the data for this row. The column of the index is ignored."""
row = index.row()
self.write_lock.acquire()
if role == PackageListItemModel.StatusUpdateRole:
self.repos[row].set_status(value)
self.dataChanged.emit(
self.index(row, 2),
self.index(row, 2),
[PackageListItemModel.StatusUpdateRole],
)
elif role == PackageListItemModel.IconUpdateRole:
self.repos[row].icon = value
self.dataChanged.emit(
self.index(row, 0),
self.index(row, 0),
[PackageListItemModel.IconUpdateRole],
)
self.write_lock.release()
def append_item(self, repo: Addon) -> None:
if repo in self.repos:
# Cowardly refuse to insert the same repo a second time
return
self.write_lock.acquire()
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.repos.append(repo)
self.endInsertRows()
self.write_lock.release()
def clear(self) -> None:
if self.rowCount() > 0:
self.write_lock.acquire()
self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1)
self.repos = []
self.endRemoveRows()
self.write_lock.release()
def update_item_status(self, name: str, status: Addon.Status) -> None:
for row, item in enumerate(self.repos):
if item.name == name:
self.setData(
self.index(row, 0), status, PackageListItemModel.StatusUpdateRole
)
return
def update_item_icon(self, name: str, icon: QIcon) -> None:
for row, item in enumerate(self.repos):
if item.name == name:
self.setData(
self.index(row, 0), icon, PackageListItemModel.IconUpdateRole
)
return
def reload_item(self, repo: Addon) -> None:
for index, item in enumerate(self.repos):
if item.name == repo.name:
self.write_lock.acquire()
self.repos[index] = repo
self.write_lock.release()
return
class CompactView(QWidget):
"""A single-line view of the package information"""
from compact_view import Ui_CompactView
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_CompactView()
self.ui.setupUi(self)
class ExpandedView(QWidget):
"""A multi-line view of the package information"""
from expanded_view import Ui_ExpandedView
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_ExpandedView()
self.ui.setupUi(self)
class PackageListItemDelegate(QStyledItemDelegate):
"""Render the repo data as a formatted region"""
def __init__(self, parent=None):
super().__init__(parent)
self.displayStyle = ListDisplayStyle.EXPANDED
self.expanded = ExpandedView()
self.compact = CompactView()
self.widget = self.expanded
def set_view(self, style: ListDisplayStyle) -> None:
if not self.displayStyle == style:
self.displayStyle = style
def sizeHint(self, option, index):
self.update_content(index)
return self.widget.sizeHint()
def update_content(self, index):
repo = index.data(PackageListItemModel.DataAccessRole)
if self.displayStyle == ListDisplayStyle.EXPANDED:
self.widget = self.expanded
self.widget.ui.labelPackageName.setText(f"<h1>{repo.display_name}</h1>")
self.widget.ui.labelIcon.setPixmap(repo.icon.pixmap(QSize(48, 48)))
else:
self.widget = self.compact
self.widget.ui.labelPackageName.setText(f"<b>{repo.display_name}</b>")
self.widget.ui.labelIcon.setPixmap(repo.icon.pixmap(QSize(16, 16)))
self.widget.ui.labelIcon.setText("")
if self.displayStyle == ListDisplayStyle.EXPANDED:
self.widget.ui.labelTags.setText("")
if repo.metadata:
self.widget.ui.labelDescription.setText(repo.metadata.Description)
self.widget.ui.labelVersion.setText(f"<i>v{repo.metadata.Version}</i>")
if self.displayStyle == ListDisplayStyle.EXPANDED:
maintainers = repo.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)
if repo.tags:
self.widget.ui.labelTags.setText(
translate("AddonsInstaller", "Tags")
+ ": "
+ ", ".join(repo.tags)
)
elif repo.macro and repo.macro.parsed:
self.widget.ui.labelDescription.setText(repo.macro.comment)
version_string = ""
if repo.macro.version:
version_string = repo.macro.version + " "
if repo.macro.on_wiki:
version_string += "(wiki)"
elif repo.macro.on_git:
version_string += "(git)"
else:
version_string += "(unknown source)"
if repo.macro.date:
version_string = (
version_string
+ ", "
+ translate("AddonsInstaller", "updated")
+ " "
+ repo.macro.date
)
self.widget.ui.labelVersion.setText("<i>" + version_string + "</i>")
if self.displayStyle == ListDisplayStyle.EXPANDED:
if repo.macro.author:
caption = translate("AddonsInstaller", "Author")
self.widget.ui.labelMaintainer.setText(
caption + ": " + repo.macro.author
)
else:
self.widget.ui.labelMaintainer.setText("")
else:
self.widget.ui.labelDescription.setText("")
self.widget.ui.labelVersion.setText("")
if self.displayStyle == ListDisplayStyle.EXPANDED:
self.widget.ui.labelMaintainer.setText("")
# Update status
if self.displayStyle == ListDisplayStyle.EXPANDED:
self.widget.ui.labelStatus.setText(self.get_expanded_update_string(repo))
else:
self.widget.ui.labelStatus.setText(self.get_compact_update_string(repo))
self.widget.adjustSize()
def get_compact_update_string(self, 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"<span {style}> ["
+ translate("AddonsInstaller", "DISABLED")
+ "]</span>"
)
return result
def get_expanded_update_string(self, 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:
installed_version_string = (
"<br/>" + translate("AddonsInstaller", "Installed version") + ": "
)
installed_version_string += repo.installed_version
else:
installed_version_string = "<br/>" + translate(
"AddonsInstaller", "Unknown version"
)
installed_date_string = ""
if repo.updated_timestamp:
installed_date_string = (
"<br/>" + translate("AddonsInstaller", "Installed on") + ": "
)
installed_date_string += (
QDateTime.fromTime_t(repo.updated_timestamp)
.date()
.toString(Qt.SystemLocaleShortDate)
)
available_version_string = ""
if repo.metadata:
available_version_string = (
"<br/>" + translate("AddonsInstaller", "Available version") + ": "
)
available_version_string += 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"<br/><span {style}>["
+ translate("AddonsInstaller", "DISABLED")
+ "]</span>"
)
return result
def paint(self, painter: QPainter, option: QStyleOptionViewItem, _: QModelIndex):
painter.save()
self.widget.resize(option.rect.size())
painter.translate(option.rect.topLeft())
self.widget.render(painter, QPoint(), QRegion(), QWidget.DrawChildren)
painter.restore()
class PackageListFilter(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(Qt.CaseInsensitive)
self.hide_obsolete = False
self.hide_py2 = False
self.hide_newer_freecad_required = False
def setPackageFilter(
self, type: int
) -> None: # 0=All, 1=Workbenches, 2=Macros, 3=Preference Packs
self.package_type = type
self.invalidateFilter()
def setStatusFilter(
self, status: int
) -> None: # 0=Any, 1=Installed, 2=Not installed, 3=Update available
self.status = status
self.invalidateFilter()
def setHidePy2(self, hide_py2: bool) -> None:
self.hide_py2 = hide_py2
self.invalidateFilter()
def setHideObsolete(self, hide_obsolete: bool) -> None:
self.hide_obsolete = hide_obsolete
self.invalidateFilter()
def setHideNewerFreeCADRequired(self, hide_nfr: bool) -> None:
self.hide_newer_freecad_required = hide_nfr
self.invalidateFilter()
def lessThan(self, left, right) -> bool:
l = self.sourceModel().data(left, PackageListItemModel.DataAccessRole)
r = self.sourceModel().data(right, PackageListItemModel.DataAccessRole)
return l.display_name.lower() < r.display_name.lower()
def filterAcceptsRow(self, row, parent=QModelIndex()):
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
# If it's not installed, check to see if it's Py2 only
if (
data.status() == Addon.Status.NOT_INSTALLED
and self.hide_py2
and data.python2
):
return False
# If it's not installed, check to see if it's marked obsolete
if (
data.status() == Addon.Status.NOT_INSTALLED
and self.hide_obsolete
and data.obsolete
):
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 = data.metadata.getFirstSupportedFreeCADVersion()
if first_supported_version is not None:
required_version = first_supported_version.split(".")
fc_major = int(FreeCAD.Version()[0])
fc_minor = int(FreeCAD.Version()[1])
if int(required_version[0]) > fc_major:
return False
elif int(required_version[0]) == fc_major and len(required_version) > 1:
if int(required_version[1]) > fc_minor:
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(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
else:
return False
else:
re = self.filterRegExp()
if re.isValid():
re.setCaseSensitivity(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
else:
return False
class Ui_PackageList(object):
"""The contents of the PackageList widget"""
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName("PackageList")
self.verticalLayout = QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.buttonCompactLayout = QToolButton(Form)
self.buttonCompactLayout.setObjectName("buttonCompactLayout")
self.buttonCompactLayout.setCheckable(True)
self.buttonCompactLayout.setAutoExclusive(True)
self.buttonCompactLayout.setIcon(
QIcon.fromTheme("expanded_view", QIcon(":/icons/compact_view.svg"))
)
self.horizontalLayout_6.addWidget(self.buttonCompactLayout)
self.buttonExpandedLayout = QToolButton(Form)
self.buttonExpandedLayout.setObjectName("buttonExpandedLayout")
self.buttonExpandedLayout.setCheckable(True)
self.buttonExpandedLayout.setChecked(True)
self.buttonExpandedLayout.setAutoExclusive(True)
self.buttonExpandedLayout.setIcon(
QIcon.fromTheme("expanded_view", QIcon(":/icons/expanded_view.svg"))
)
self.horizontalLayout_6.addWidget(self.buttonExpandedLayout)
self.labelPackagesContaining = QLabel(Form)
self.labelPackagesContaining.setObjectName("labelPackagesContaining")
self.horizontalLayout_6.addWidget(self.labelPackagesContaining)
self.comboPackageType = QComboBox(Form)
self.comboPackageType.addItem("")
self.comboPackageType.addItem("")
self.comboPackageType.addItem("")
self.comboPackageType.addItem("")
self.comboPackageType.setObjectName("comboPackageType")
self.horizontalLayout_6.addWidget(self.comboPackageType)
self.labelStatus = QLabel(Form)
self.labelStatus.setObjectName("labelStatus")
self.horizontalLayout_6.addWidget(self.labelStatus)
self.comboStatus = QComboBox(Form)
self.comboStatus.addItem("")
self.comboStatus.addItem("")
self.comboStatus.addItem("")
self.comboStatus.addItem("")
self.comboStatus.setObjectName("comboStatus")
self.horizontalLayout_6.addWidget(self.comboStatus)
self.lineEditFilter = QLineEdit(Form)
self.lineEditFilter.setObjectName("lineEditFilter")
self.lineEditFilter.setClearButtonEnabled(True)
self.horizontalLayout_6.addWidget(self.lineEditFilter)
self.labelFilterValidity = QLabel(Form)
self.labelFilterValidity.setObjectName("labelFilterValidity")
self.horizontalLayout_6.addWidget(self.labelFilterValidity)
self.verticalLayout.addLayout(self.horizontalLayout_6)
self.listPackages = QListView(Form)
self.listPackages.setObjectName("listPackages")
self.listPackages.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.listPackages.setProperty("showDropIndicator", False)
self.listPackages.setSelectionMode(QAbstractItemView.NoSelection)
self.listPackages.setResizeMode(QListView.Adjust)
self.listPackages.setUniformItemSizes(False)
self.listPackages.setAlternatingRowColors(True)
self.listPackages.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.verticalLayout.addWidget(self.listPackages)
self.retranslateUi(Form)
QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
self.labelPackagesContaining.setText(
QCoreApplication.translate(
"AddonsInstaller", "Show Addons containing:", None
)
)
self.comboPackageType.setItemText(
0, QCoreApplication.translate("AddonsInstaller", "All", None)
)
self.comboPackageType.setItemText(
1, QCoreApplication.translate("AddonsInstaller", "Workbenches", None)
)
self.comboPackageType.setItemText(
2, QCoreApplication.translate("AddonsInstaller", "Macros", None)
)
self.comboPackageType.setItemText(
3, QCoreApplication.translate("AddonsInstaller", "Preference Packs", None)
)
self.labelStatus.setText(
QCoreApplication.translate("AddonsInstaller", "Status:", None)
)
self.comboStatus.setItemText(
StatusFilter.ANY, QCoreApplication.translate("AddonsInstaller", "Any", None)
)
self.comboStatus.setItemText(
StatusFilter.INSTALLED,
QCoreApplication.translate("AddonsInstaller", "Installed", None),
)
self.comboStatus.setItemText(
StatusFilter.NOT_INSTALLED,
QCoreApplication.translate("AddonsInstaller", "Not installed", None),
)
self.comboStatus.setItemText(
StatusFilter.UPDATE_AVAILABLE,
QCoreApplication.translate("AddonsInstaller", "Update available", None),
)
self.lineEditFilter.setPlaceholderText(
QCoreApplication.translate("AddonsInstaller", "Filter", None)
)
self.labelFilterValidity.setText(
QCoreApplication.translate("AddonsInstaller", "OK", None)
)