Addon Manager: Switch custom repo prefs to a table
This commit is contained in:
@@ -28,29 +28,65 @@ import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from PySide2 import QtCore
|
||||
from PySide2.QtGui import QIcon
|
||||
from PySide2.QtWidgets import (
|
||||
QWidget,
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QHeaderView,
|
||||
QRadioButton,
|
||||
QLineEdit,
|
||||
QTextEdit,
|
||||
)
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
#pylint: disable=too-few-public-methods
|
||||
|
||||
class AddonManagerOptions:
|
||||
""" A class containing a form element that is inserted as a FreeCAD preference page. """
|
||||
"""A class containing a form element that is inserted as a FreeCAD preference page."""
|
||||
|
||||
def __init__(self, _=None):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(os.path.dirname(__file__), "AddonManagerOptions.ui")
|
||||
)
|
||||
self.table_model = CustomRepoDataModel()
|
||||
self.form.customRepositoriesTableView.setModel(self.table_model)
|
||||
|
||||
self.form.addCustomRepositoryButton.setIcon(
|
||||
QIcon.fromTheme("add", QIcon(":/icons/list-add.svg"))
|
||||
)
|
||||
self.form.removeCustomRepositoryButton.setIcon(
|
||||
QIcon.fromTheme("remove", QIcon(":/icons/list-remove.svg"))
|
||||
)
|
||||
|
||||
self.form.customRepositoriesTableView.horizontalHeader().setStretchLastSection(
|
||||
False
|
||||
)
|
||||
self.form.customRepositoriesTableView.horizontalHeader().setSectionResizeMode(
|
||||
0, QHeaderView.Stretch
|
||||
)
|
||||
self.form.customRepositoriesTableView.horizontalHeader().setSectionResizeMode(
|
||||
1, QHeaderView.ResizeToContents
|
||||
)
|
||||
|
||||
self.form.addCustomRepositoryButton.clicked.connect(
|
||||
self._add_custom_repo_clicked
|
||||
)
|
||||
self.form.removeCustomRepositoryButton.clicked.connect(
|
||||
self._remove_custom_repo_clicked
|
||||
)
|
||||
self.form.customRepositoriesTableView.doubleClicked.connect(
|
||||
self._row_double_clicked
|
||||
)
|
||||
|
||||
def saveSettings(self):
|
||||
"""Required function: called by the preferences dialog when Apply or Save is clicked,
|
||||
saves out the preference data by reading it from the widgets."""
|
||||
for widget in self.form.children():
|
||||
self.recursive_widget_saver(widget)
|
||||
self.table_model.save_model()
|
||||
|
||||
def recursive_widget_saver(self, widget):
|
||||
"""Writes out the data for this widget and all of its children, recursively."""
|
||||
@@ -79,7 +115,7 @@ class AddonManagerOptions:
|
||||
text = widget.text()
|
||||
pref.SetString(str(pref_entry, "utf-8"), text)
|
||||
elif widget.metaObject().className() == "Gui::PrefFileChooser":
|
||||
filename = str(widget.property("fileName"), "utf-8")
|
||||
filename = str(widget.property("fileName"))
|
||||
filename = pref.SetString(str(pref_entry, "utf-8"), filename)
|
||||
|
||||
# Recurse over children
|
||||
@@ -92,6 +128,7 @@ class AddonManagerOptions:
|
||||
loads the preference data and assigns it to the widgets."""
|
||||
for widget in self.form.children():
|
||||
self.recursive_widget_loader(widget)
|
||||
self.table_model.load_model()
|
||||
|
||||
def recursive_widget_loader(self, widget):
|
||||
"""Loads the data for this widget and all of its children, recursively."""
|
||||
@@ -126,3 +163,160 @@ class AddonManagerOptions:
|
||||
if isinstance(widget, QtCore.QObject):
|
||||
for child in widget.children():
|
||||
self.recursive_widget_loader(child)
|
||||
|
||||
def _add_custom_repo_clicked(self):
|
||||
"""Callback: show the Add custom repo dialog"""
|
||||
dlg = CustomRepositoryDialog()
|
||||
url, branch = dlg.exec()
|
||||
if url and branch:
|
||||
self.table_model.appendData(url, branch)
|
||||
|
||||
def _remove_custom_repo_clicked(self):
|
||||
"""Callback: when the remove button is clicked, get the current selection and remove it."""
|
||||
item = self.form.customRepositoriesTableView.currentIndex()
|
||||
if not item.isValid():
|
||||
return
|
||||
row = item.row()
|
||||
self.table_model.removeRows(row, 1, QtCore.QModelIndex())
|
||||
|
||||
def _row_double_clicked(self, item):
|
||||
"""Edit the row that was double-clicked"""
|
||||
row = item.row()
|
||||
dlg = CustomRepositoryDialog()
|
||||
url_index = self.table_model.createIndex(row, 0)
|
||||
branch_index = self.table_model.createIndex(row, 1)
|
||||
dlg.dialog.urlLineEdit.setText(self.table_model.data(url_index))
|
||||
dlg.dialog.branchLineEdit.setText(self.table_model.data(branch_index))
|
||||
url, branch = dlg.exec()
|
||||
if url and branch:
|
||||
self.table_model.setData(url_index, url)
|
||||
self.table_model.setData(branch_index, branch)
|
||||
|
||||
|
||||
class CustomRepoDataModel(QtCore.QAbstractTableModel):
|
||||
"""The model for the custom repositories: wraps the underlying preference data and uses that
|
||||
as its main data store."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
pref_access_string = "User parameter:BaseApp/Preferences/Addons"
|
||||
self.pref = FreeCAD.ParamGet(pref_access_string)
|
||||
self.load_model()
|
||||
|
||||
def load_model(self):
|
||||
"""Load the data from the preferences entry"""
|
||||
pref_entry: str = self.pref.GetString("CustomRepositories", "")
|
||||
|
||||
# The entry is saved as a space- and newline-delimited text block: break it into its
|
||||
# constituent parts
|
||||
lines = pref_entry.split("\n")
|
||||
self.model = []
|
||||
for line in lines:
|
||||
if not line:
|
||||
continue
|
||||
split_data = line.split()
|
||||
if len(split_data) > 1:
|
||||
branch = split_data[1]
|
||||
else:
|
||||
branch = "master"
|
||||
url = split_data[0]
|
||||
self.model.append([url, branch])
|
||||
|
||||
def save_model(self):
|
||||
"""Save the data into a preferences entry"""
|
||||
entry = ""
|
||||
for row in self.model:
|
||||
entry += f"{row[0]} {row[1]}\n"
|
||||
self.pref.SetString("CustomRepositories", entry)
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int:
|
||||
"""The number of rows"""
|
||||
if parent.isValid():
|
||||
return 0
|
||||
return len(self.model)
|
||||
|
||||
def columnCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int:
|
||||
"""The number of columns (which is always 2)"""
|
||||
if parent.isValid():
|
||||
return 0
|
||||
return 2
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
"""The data at an index."""
|
||||
if role != QtCore.Qt.DisplayRole:
|
||||
return None
|
||||
row = index.row()
|
||||
column = index.column()
|
||||
if row > len(self.model):
|
||||
return None
|
||||
if column > 1:
|
||||
return None
|
||||
return self.model[row][column]
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
"""Get the row and column header data."""
|
||||
if role != QtCore.Qt.DisplayRole:
|
||||
return None
|
||||
if orientation == QtCore.Qt.Vertical:
|
||||
return section + 1
|
||||
if section == 0:
|
||||
return translate(
|
||||
"AddonsInstaller",
|
||||
"Repository URL",
|
||||
"Preferences header for custom repositories",
|
||||
)
|
||||
if section == 1:
|
||||
return translate(
|
||||
"AddonsInstaller",
|
||||
"Branch name",
|
||||
"Preferences header for custom repositories",
|
||||
)
|
||||
return None
|
||||
|
||||
def removeRows(self, row, count, parent):
|
||||
"""Remove rows"""
|
||||
self.beginRemoveRows(parent, row, row + count - 1)
|
||||
for _ in range(count):
|
||||
self.model.pop(row)
|
||||
self.endRemoveRows()
|
||||
|
||||
def insertRows(self, row, count, parent):
|
||||
"""Insert blank rows"""
|
||||
self.beginInsertRows(parent, row, row + count - 1)
|
||||
for _ in range(count):
|
||||
self.model.insert(["", ""])
|
||||
self.endInsertRows()
|
||||
|
||||
def appendData(self, url, branch):
|
||||
"""Append this url and branch to the end of the list"""
|
||||
row = self.rowCount()
|
||||
self.beginInsertRows(QtCore.QModelIndex(), row, row)
|
||||
self.model.append([url, branch])
|
||||
self.endInsertRows()
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
"""Set the data at this index"""
|
||||
if role != QtCore.Qt.EditRole:
|
||||
return
|
||||
self.model[index.row()][index.column()] = value
|
||||
self.dataChanged.emit(index, index)
|
||||
|
||||
|
||||
class CustomRepositoryDialog:
|
||||
"""A dialog for setting up a custom repository, with branch information"""
|
||||
|
||||
def __init__(self):
|
||||
self.dialog = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), "AddonManagerOptions_AddCustomRepository.ui"
|
||||
)
|
||||
)
|
||||
|
||||
def exec(self):
|
||||
"""Run the dialog modally, and return either None or a tuple or (url,branch)"""
|
||||
result = self.dialog.exec()
|
||||
if result == QDialog.Accepted:
|
||||
url = self.dialog.urlLineEdit.text()
|
||||
branch = self.dialog.branchLineEdit.text()
|
||||
return (url, branch)
|
||||
return (None, None)
|
||||
|
||||
@@ -140,44 +140,64 @@ installed addons will be checked for available updates
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom repositories (one per line):</string>
|
||||
<string>Custom repositories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefTextEdit" name="guipreftexteditcustomrepositories">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QTableView" name="customRepositoriesTableView">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>You can use this window to specify additional addon repositories
|
||||
to be scanned for available addons. To include a specific branch, add it to the end
|
||||
of the line after a space (e.g. https://github.com/FreeCAD/FreeCAD master).</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>CustomRepositories</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Addons</cstring>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="addCustomRepositoryButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="removeCustomRepositoryButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
@@ -399,11 +419,6 @@ of the line after a space (e.g. https://github.com/FreeCAD/FreeCAD master).</str
|
||||
<extends>QComboBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefRadioButton</class>
|
||||
<extends>QRadioButton</extends>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddCustomRepositoryDialog</class>
|
||||
<widget class="QDialog" name="AddCustomRepositoryDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>95</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Custom repository</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Repository URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="urlLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Branch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="branchLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AddCustomRepositoryDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AddCustomRepositoryDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -22,6 +22,7 @@ SET(AddonManager_SRCS
|
||||
addonmanager_workers_startup.py
|
||||
addonmanager_workers_utility.py
|
||||
AddonManagerOptions.ui
|
||||
AddonManagerOptions_AddCustomRepository.ui
|
||||
AddonManagerOptions.py
|
||||
ALLOWED_PYTHON_PACKAGES.txt
|
||||
change_branch.py
|
||||
|
||||
Reference in New Issue
Block a user