Addon Manager: Add person editor
This commit is contained in:
@@ -11,6 +11,7 @@ SET(AddonManager_SRCS
|
||||
addonmanager_devmode_add_content.py
|
||||
addonmanager_devmode_license_selector.py
|
||||
addonmanager_devmode_dependencies.py
|
||||
addonmanager_devmode_person_editor.py
|
||||
addonmanager_git.py
|
||||
addonmanager_macro.py
|
||||
addonmanager_utilities.py
|
||||
@@ -28,6 +29,7 @@ SET(AddonManager_SRCS
|
||||
developer_mode_copyright_info.ui
|
||||
developer_mode_dependencies.ui
|
||||
developer_mode_license.ui
|
||||
developer_mode_people.ui
|
||||
developer_mode_select_from_list.ui
|
||||
expanded_view.py
|
||||
first_run.ui
|
||||
|
||||
@@ -23,22 +23,28 @@
|
||||
""" Classes to manage "Developer Mode" """
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from PySide2.QtWidgets import QFileDialog, QTableWidgetItem, QDialog
|
||||
from PySide2.QtGui import QIcon, QValidator, QRegularExpressionValidator, QPixmap, QDesktopServices
|
||||
from PySide2.QtCore import QRegularExpression, QUrl, QFile, QIODevice
|
||||
from PySide2.QtWidgets import QFileDialog, QTableWidgetItem
|
||||
from PySide2.QtGui import (
|
||||
QIcon,
|
||||
QValidator,
|
||||
QRegularExpressionValidator,
|
||||
QPixmap,
|
||||
)
|
||||
from PySide2.QtCore import QRegularExpression, Qt
|
||||
from addonmanager_git import GitManager
|
||||
|
||||
from addonmanager_devmode_license_selector import LicenseSelector
|
||||
from addonmanager_devmode_person_editor import PersonEditor
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
|
||||
class AddonGitInterface:
|
||||
"""Wrapper to handle the git calls needed by this class"""
|
||||
|
||||
@@ -153,6 +159,13 @@ class DeveloperMode:
|
||||
"""The main Developer Mode dialog, for editing package.xml metadata graphically."""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# In the UI we want to show a translated string for the person type, but the underlying
|
||||
# string must be the one expected by the metadata parser, in English
|
||||
self.person_type_translation = {
|
||||
"maintainer": translate("AddonsInstaller", "Maintainer"),
|
||||
"author": translate("AddonsInstaller", "Author"),
|
||||
}
|
||||
self.dialog = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(os.path.dirname(__file__), "developer_mode.ui")
|
||||
)
|
||||
@@ -246,22 +259,12 @@ class DeveloperMode:
|
||||
for maintainer in metadata.Maintainer:
|
||||
name = maintainer["name"]
|
||||
email = maintainer["email"]
|
||||
self.dialog.peopleTableWidget.insertRow(row)
|
||||
self.dialog.peopleTableWidget.setItem(
|
||||
row, 0, QTableWidgetItem(translate("AddonsInstaller", "Maintainer"))
|
||||
)
|
||||
self.dialog.peopleTableWidget.setItem(row, 1, QTableWidgetItem(name))
|
||||
self.dialog.peopleTableWidget.setItem(row, 2, QTableWidgetItem(email))
|
||||
self._add_person_row(row, "maintainer", name, email)
|
||||
row += 1
|
||||
for author in metadata.Author:
|
||||
name = author["name"]
|
||||
email = author["email"]
|
||||
self.dialog.peopleTableWidget.insertRow(row)
|
||||
self.dialog.peopleTableWidget.setItem(
|
||||
row, 0, QTableWidgetItem(translate("AddonsInstaller", "Author"))
|
||||
)
|
||||
self.dialog.peopleTableWidget.setItem(row, 1, QTableWidgetItem(name))
|
||||
self.dialog.peopleTableWidget.setItem(row, 2, QTableWidgetItem(email))
|
||||
self._add_person_row(row, "author", name, email)
|
||||
row += 1
|
||||
|
||||
if row == 0:
|
||||
@@ -273,6 +276,15 @@ class DeveloperMode:
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
def _add_person_row(self, row, person_type, name, email):
|
||||
"""Add this person to the peopleTableWidget at row given"""
|
||||
self.dialog.peopleTableWidget.insertRow(row)
|
||||
item = QTableWidgetItem(self.person_type_translation[person_type])
|
||||
item.setData(Qt.UserRole, person_type)
|
||||
self.dialog.peopleTableWidget.setItem(row, 0, item)
|
||||
self.dialog.peopleTableWidget.setItem(row, 1, QTableWidgetItem(name))
|
||||
self.dialog.peopleTableWidget.setItem(row, 2, QTableWidgetItem(email))
|
||||
|
||||
def _populate_licenses_from_metadata(self, metadata):
|
||||
"""Use the passed metadata object to populate the licenses"""
|
||||
self.dialog.licensesTableWidget.setRowCount(0)
|
||||
@@ -432,12 +444,22 @@ class DeveloperMode:
|
||||
self.dialog.pathToAddonComboBox.editTextChanged.connect(
|
||||
self._addon_combo_text_changed
|
||||
)
|
||||
|
||||
self.dialog.addLicenseToolButton.clicked.connect(self._add_license_clicked)
|
||||
self.dialog.removeLicenseToolButton.clicked.connect(self._remove_license_clicked)
|
||||
self.dialog.removeLicenseToolButton.clicked.connect(
|
||||
self._remove_license_clicked
|
||||
)
|
||||
self.dialog.licensesTableWidget.itemSelectionChanged.connect(
|
||||
self._license_selection_changed
|
||||
)
|
||||
self.dialog.licensesTableWidget.itemDoubleClicked.connect(self._edit_license)
|
||||
|
||||
self.dialog.addPersonToolButton.clicked.connect(self._add_person_clicked)
|
||||
self.dialog.removePersonToolButton.clicked.connect(self._remove_person_clicked)
|
||||
self.dialog.peopleTableWidget.itemSelectionChanged.connect(self._person_selection_changed)
|
||||
self.dialog.licensesTableWidget.itemSelectionChanged.connect(self._license_selection_changed)
|
||||
self.dialog.peopleTableWidget.itemSelectionChanged.connect(
|
||||
self._person_selection_changed
|
||||
)
|
||||
self.dialog.peopleTableWidget.itemDoubleClicked.connect(self._edit_person)
|
||||
|
||||
# Finally, populate the combo boxes, etc.
|
||||
self._populate_combo()
|
||||
@@ -552,8 +574,21 @@ class DeveloperMode:
|
||||
# the first entry
|
||||
self.dialog.licensesTableWidget.removeRow(items[0].row())
|
||||
|
||||
def _edit_license(self, item):
|
||||
row = item.row()
|
||||
short_code = self.dialog.licensesTableWidget.item(row, 0).text()
|
||||
path = self.dialog.licensesTableWidget.item(row, 1).text()
|
||||
license_selector = LicenseSelector(self.current_mod)
|
||||
short_code, path = license_selector.exec(short_code, path)
|
||||
if short_code:
|
||||
self.dialog.licensesTableWidget.removeRow(row)
|
||||
self._add_license_row(row, short_code, path)
|
||||
|
||||
def _add_person_clicked(self):
|
||||
pass
|
||||
dlg = PersonEditor()
|
||||
person_type, name, email = dlg.exec()
|
||||
if person_type and name:
|
||||
self._add_person_row(row, person_type, name, email)
|
||||
|
||||
def _remove_person_clicked(self):
|
||||
items = self.dialog.peopleTableWidget.selectedIndexes()
|
||||
@@ -562,3 +597,17 @@ class DeveloperMode:
|
||||
# the first entry
|
||||
self.dialog.peopleTableWidget.removeRow(items[0].row())
|
||||
|
||||
def _edit_person(self, item):
|
||||
row = item.row()
|
||||
person_type = self.dialog.peopleTableWidget.item(row, 0).data(Qt.UserRole)
|
||||
name = self.dialog.peopleTableWidget.item(row, 1).text()
|
||||
email = self.dialog.peopleTableWidget.item(row, 2).text()
|
||||
|
||||
dlg = PersonEditor()
|
||||
dlg.setup(person_type, name, email)
|
||||
person_type, name, email = dlg.exec()
|
||||
|
||||
if person_type and name:
|
||||
self.dialog.peopleTableWidget.removeRow(row)
|
||||
self._add_person_row(row, person_type, name, email)
|
||||
self.dialog.peopleTableWidget.selectRow(row)
|
||||
|
||||
@@ -114,15 +114,14 @@ class LicenseSelector:
|
||||
short_code = self.pref.GetString("devModeLastSelectedLicense", "LGPLv2.1")
|
||||
self.set_license(short_code)
|
||||
|
||||
def exec(self, short_code: str = None, license_path: str = None) -> Optional[str]:
|
||||
def exec(self, short_code: str = None, license_path: str = "") -> Optional[str]:
|
||||
"""The main method for executing this dialog, as a modal that returns a tuple of the
|
||||
license's "short code" and optionally the path to the license file. Returns a tuple
|
||||
of None,None if the user cancels the operation."""
|
||||
|
||||
if short_code:
|
||||
self.set_license(short_code)
|
||||
if license_path:
|
||||
self.dialog.pathLineEdit.setText(license_path)
|
||||
self.dialog.pathLineEdit.setText(license_path)
|
||||
result = self.dialog.exec()
|
||||
if result == QDialog.Accepted:
|
||||
new_short_code = self.dialog.comboBox.currentData()
|
||||
@@ -188,17 +187,20 @@ class LicenseSelector:
|
||||
dir=start_dir,
|
||||
)
|
||||
if license_path:
|
||||
self._set_path(start_dir, license_path)
|
||||
self._set_path(self.path_to_addon, license_path)
|
||||
|
||||
def _set_path(self, start_dir: str, license_path: str):
|
||||
"""Sets the value displayed in the path widget to the relative path from
|
||||
start_dir to license_path"""
|
||||
license_path = license_path.replace("/", os.path.sep)
|
||||
if not license_path.startswith(start_dir.replace("/", os.path.sep)):
|
||||
base_dir = start_dir.replace("/", os.path.sep)
|
||||
if base_dir[-1] != os.path.sep:
|
||||
base_dir += os.path.sep
|
||||
if not license_path.startswith(base_dir):
|
||||
FreeCAD.Console.PrintError("Selected file not in Addon\n")
|
||||
# Eventually offer to copy it...
|
||||
# Eventually offer to copy it?
|
||||
return
|
||||
relative_path = license_path[len(start_dir) :]
|
||||
relative_path = license_path[len(base_dir) :]
|
||||
relative_path = relative_path.replace(os.path.sep, "/")
|
||||
self.dialog.pathLineEdit.setText(relative_path)
|
||||
|
||||
|
||||
77
src/Mod/AddonManager/addonmanager_devmode_person_editor.py
Normal file
77
src/Mod/AddonManager/addonmanager_devmode_person_editor.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022 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/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
""" Contains a class to handle editing a person (from a Metadata standpoint). """
|
||||
|
||||
import os
|
||||
from typing import Tuple # Needed until Py 3.9, when tuple supports this directly
|
||||
|
||||
from PySide2.QtWidgets import QDialog
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class PersonEditor:
|
||||
"""Create or edit a maintainer or author record."""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.dialog = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(os.path.dirname(__file__), "developer_mode_people.ui")
|
||||
)
|
||||
self.dialog.comboBox.clear()
|
||||
self.dialog.comboBox.addItem(
|
||||
translate("AddonsInstaller", "Maintainer"), userData="maintainer"
|
||||
)
|
||||
self.dialog.comboBox.addItem(
|
||||
translate("AddonsInstaller", "Author"), userData="author"
|
||||
)
|
||||
|
||||
def exec(self) -> Tuple[str, str, str]:
|
||||
"""Run the dialog, and return a tuple of the person's record type, their name, and their
|
||||
email address. Email may be None. If the others are None it's because the user cancelled
|
||||
the interaction."""
|
||||
result = self.dialog.exec()
|
||||
if result == QDialog.Accepted:
|
||||
return (
|
||||
self.dialog.comboBox.currentData(),
|
||||
self.dialog.nameLineEdit.text(),
|
||||
self.dialog.emailLineEdit.text(),
|
||||
)
|
||||
return (None, None, None)
|
||||
|
||||
def setup(
|
||||
self, person_type: str = "maintainer", name: str = "", email: str = ""
|
||||
) -> None:
|
||||
"""Configure the dialog"""
|
||||
index = self.dialog.comboBox.findData(person_type)
|
||||
if index == -1:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"Internal Error: unrecognized person type {person_type}"
|
||||
)
|
||||
index = 0
|
||||
self.dialog.comboBox.setCurrentIndex(index)
|
||||
self.dialog.nameLineEdit.setText(name)
|
||||
self.dialog.emailLineEdit.setText(email)
|
||||
99
src/Mod/AddonManager/developer_mode_people.ui
Normal file
99
src/Mod/AddonManager/developer_mode_people.ui
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>personDialog</class>
|
||||
<widget class="QDialog" name="personDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>313</width>
|
||||
<height>118</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add Person</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="toolTip">
|
||||
<string>A maintainer is someone with current commit access on this project. An author is anyone else you'd like to give credit to.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="nameLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Email:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="emailLineEdit">
|
||||
<property name="toolTip">
|
||||
<string>Email is required for maintainers, and optional for authors.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>personDialog</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>personDialog</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>
|
||||
Reference in New Issue
Block a user