FreeCAD

Форк
0
/
addonmanager_devmode_add_content.py 
631 строка · 27.3 Кб
1
# SPDX-License-Identifier: LGPL-2.1-or-later
2
# ***************************************************************************
3
# *                                                                         *
4
# *   Copyright (c) 2022 FreeCAD Project Association                        *
5
# *                                                                         *
6
# *   This file is part of FreeCAD.                                         *
7
# *                                                                         *
8
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
# *   under the terms of the GNU Lesser General Public License as           *
10
# *   published by the Free Software Foundation, either version 2.1 of the  *
11
# *   License, or (at your option) any later version.                       *
12
# *                                                                         *
13
# *   FreeCAD is distributed in the hope that it will be useful, but        *
14
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
# *   Lesser General Public License for more details.                       *
17
# *                                                                         *
18
# *   You should have received a copy of the GNU Lesser General Public      *
19
# *   License along with FreeCAD. If not, see                               *
20
# *   <https://www.gnu.org/licenses/>.                                      *
21
# *                                                                         *
22
# ***************************************************************************
23

24
""" Contains a class for adding a single content item, as well as auxiliary classes for
25
its dependent dialog boxes. """
26

27
import os
28
from typing import Optional, Tuple, List
29

30
import FreeCAD
31
import FreeCADGui
32

33
from Addon import INTERNAL_WORKBENCHES
34

35
from PySide.QtWidgets import (
36
    QDialog,
37
    QLayout,
38
    QFileDialog,
39
    QTableWidgetItem,
40
    QSizePolicy,
41
)
42
from PySide.QtGui import QIcon
43
from PySide.QtCore import Qt
44

45
from addonmanager_devmode_validators import (
46
    VersionValidator,
47
    NameValidator,
48
    PythonIdentifierValidator,
49
)
50
from addonmanager_devmode_people_table import PeopleTable
51
from addonmanager_devmode_licenses_table import LicensesTable
52

53
# pylint: disable=too-few-public-methods
54

55
translate = FreeCAD.Qt.translate
56

57

58
class AddContent:
59
    """A dialog for adding a single content item to the package metadata."""
60

61
    def __init__(self, path_to_addon: str, toplevel_metadata: FreeCAD.Metadata):
62
        """path_to_addon is the full path to the toplevel directory of this Addon, and
63
        toplevel_metadata is to overall package.xml Metadata object for this Addon. This
64
        information is used to assist the use in filling out the dialog by providing
65
        sensible default values."""
66
        self.dialog = FreeCADGui.PySideUic.loadUi(
67
            os.path.join(os.path.dirname(__file__), "developer_mode_add_content.ui")
68
        )
69
        # These are in alphabetical order in English, but their actual label may be translated in
70
        # the GUI. Store their underlying type as user data.
71
        self.dialog.addonKindComboBox.setItemData(0, "macro")
72
        self.dialog.addonKindComboBox.setItemData(1, "preferencepack")
73
        self.dialog.addonKindComboBox.setItemData(2, "workbench")
74

75
        self.people_table = PeopleTable()
76
        self.licenses_table = LicensesTable()
77
        large_size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
78
        large_size_policy.setHorizontalStretch(2)
79
        small_size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
80
        small_size_policy.setHorizontalStretch(1)
81
        self.people_table.widget.setSizePolicy(large_size_policy)
82
        self.licenses_table.widget.setSizePolicy(small_size_policy)
83
        self.dialog.peopleAndLicenseshorizontalLayout.addWidget(self.people_table.widget)
84
        self.dialog.peopleAndLicenseshorizontalLayout.addWidget(self.licenses_table.widget)
85

86
        self.toplevel_metadata = toplevel_metadata
87
        self.metadata = None
88
        self.path_to_addon = path_to_addon.replace("/", os.path.sep)
89
        if self.path_to_addon[-1] != os.path.sep:
90
            self.path_to_addon += os.path.sep  # Make sure the path ends with a separator
91

92
        self.dialog.iconLabel.hide()  # Until we have an icon to display
93

94
        self.dialog.iconBrowseButton.clicked.connect(self._browse_for_icon_clicked)
95
        self.dialog.subdirectoryBrowseButton.clicked.connect(self._browse_for_subdirectory_clicked)
96
        self.dialog.tagsButton.clicked.connect(self._tags_clicked)
97
        self.dialog.dependenciesButton.clicked.connect(self._dependencies_clicked)
98
        self.dialog.freecadVersionsButton.clicked.connect(self._freecad_versions_clicked)
99

100
        self.dialog.versionLineEdit.setValidator(VersionValidator())
101
        self.dialog.prefPackNameLineEdit.setValidator(NameValidator())
102
        self.dialog.displayNameLineEdit.setValidator(NameValidator())
103
        self.dialog.workbenchClassnameLineEdit.setValidator(PythonIdentifierValidator())
104

105
    def exec(
106
        self,
107
        content_kind: str = "workbench",
108
        metadata: FreeCAD.Metadata = None,
109
        singleton: bool = True,
110
    ) -> Optional[Tuple[str, FreeCAD.Metadata]]:
111
        """Execute the dialog as a modal, returning a new Metadata object if the dialog
112
        is accepted, or None if it is rejected. This metadata object represents a single
113
        new content item. It's returned as a tuple with the object type as the first component,
114
        and the metadata object itself as the second."""
115
        if metadata:
116
            self.metadata = FreeCAD.Metadata(metadata)  # Deep copy
117
        else:
118
            self.metadata = FreeCAD.Metadata()
119
        self.dialog.singletonCheckBox.setChecked(singleton)
120
        if singleton:
121
            # This doesn't happen automatically the first time
122
            self.dialog.otherMetadataGroupBox.hide()
123
        index = self.dialog.addonKindComboBox.findData(content_kind)
124
        if index == -1:
125
            index = 2  # Workbench
126
            FreeCAD.Console.PrintWarning(
127
                translate("AddonsInstaller", "Unrecognized content kind '{}'").format(content_kind)
128
                + "\n"
129
            )
130
        self.dialog.addonKindComboBox.setCurrentIndex(index)
131
        if metadata:
132
            self._populate_dialog(metadata)
133

134
        self.dialog.layout().setSizeConstraint(QLayout.SetFixedSize)
135
        result = self.dialog.exec()
136
        if result == QDialog.Accepted:
137
            return self._generate_metadata()
138
        return None
139

140
    def _populate_dialog(self, metadata: FreeCAD.Metadata) -> None:
141
        """Fill in the dialog with the details from the passed metadata object"""
142
        addon_kind = self.dialog.addonKindComboBox.currentData()
143
        if addon_kind == "workbench":
144
            self.dialog.workbenchClassnameLineEdit.setText(metadata.Classname)
145
        elif addon_kind == "macro":
146
            files = self.metadata.File
147
            if files:
148
                self.dialog.macroFileLineEdit.setText(files[0])
149
        elif addon_kind == "preferencepack":
150
            self.dialog.prefPackNameLineEdit.setText(self.metadata.Name)
151
        else:
152
            raise RuntimeError("Invalid data found for selection")
153

154
        # Now set the rest of it
155
        if metadata.Icon:
156
            self._set_icon(metadata.Icon)
157
        elif self.toplevel_metadata.Icon:
158
            if metadata.Subdirectory and metadata.Subdirectory != "./":
159
                self._set_icon("../" + self.toplevel_metadata.Icon)
160
            else:
161
                self._set_icon(self.toplevel_metadata.Icon)
162
        else:
163
            self.dialog.iconLabel.hide()
164
            self.dialog.iconLineEdit.setText("")
165

166
        if metadata.Subdirectory:
167
            self.dialog.subdirectoryLineEdit.setText(metadata.Subdirectory)
168
        else:
169
            self.dialog.subdirectoryLineEdit.setText("")
170

171
        self.dialog.displayNameLineEdit.setText(metadata.Name)
172
        self.dialog.descriptionTextEdit.setPlainText(metadata.Description)
173
        self.dialog.versionLineEdit.setText(metadata.Version)
174

175
        self.people_table.show(metadata)
176
        self.licenses_table.show(metadata, self.path_to_addon)
177

178
    def _set_icon(self, icon_relative_path):
179
        """Load the icon and display it, and its path, in the dialog."""
180
        icon_path = os.path.join(self.path_to_addon, icon_relative_path.replace("/", os.path.sep))
181
        if os.path.isfile(icon_path):
182
            icon_data = QIcon(icon_path)
183
            if not icon_data.isNull():
184
                self.dialog.iconLabel.setPixmap(icon_data.pixmap(32, 32))
185
                self.dialog.iconLabel.show()
186
        else:
187
            FreeCAD.Console.PrintError(
188
                translate("AddonsInstaller", "Unable to locate icon at {}").format(icon_path) + "\n"
189
            )
190
        self.dialog.iconLineEdit.setText(icon_relative_path)
191

192
    def _generate_metadata(self) -> Tuple[str, FreeCAD.Metadata]:
193
        """Create and return a new metadata object based on the contents of the dialog."""
194

195
        if not self.metadata:
196
            self.metadata = FreeCAD.Metadata()
197

198
        ##########################################################################################
199
        # Required data:
200
        current_data: str = self.dialog.addonKindComboBox.currentData()
201
        if current_data == "preferencepack":
202
            self.metadata.Name = self.dialog.prefPackNameLineEdit.text()
203
        elif self.dialog.displayNameLineEdit.text():
204
            self.metadata.Name = self.dialog.displayNameLineEdit.text()
205

206
        if current_data == "workbench":
207
            self.metadata.Classname = self.dialog.workbenchClassnameLineEdit.text()
208
        elif current_data == "macro":
209
            self.metadata.File = [self.dialog.macroFileLineEdit.text()]
210
        ##########################################################################################
211

212
        self.metadata.Subdirectory = self.dialog.subdirectoryLineEdit.text()
213
        self.metadata.Icon = self.dialog.iconLineEdit.text()
214

215
        # Early return if this is the only addon
216
        if self.dialog.singletonCheckBox.isChecked():
217
            return current_data, self.metadata
218

219
        # Otherwise, process the rest of the metadata (display name is already done)
220
        self.metadata.Description = self.dialog.descriptionTextEdit.document().toPlainText()
221
        self.metadata.Version = self.dialog.versionLineEdit.text()
222

223
        maintainers = []
224
        authors = []
225
        for row in range(self.dialog.peopleTableWidget.rowCount()):
226
            person_type = self.dialog.peopleTableWidget.item(row, 0).data()
227
            name = self.dialog.peopleTableWidget.item(row, 1).text()
228
            email = self.dialog.peopleTableWidget.item(row, 2).text()
229
            if person_type == "maintainer":
230
                maintainers.append({"name": name, "email": email})
231
            elif person_type == "author":
232
                authors.append({"name": name, "email": email})
233
        self.metadata.Maintainer = maintainers
234
        self.metadata.Author = authors
235

236
        licenses = []
237
        for row in range(self.dialog.licensesTableWidget.rowCount()):
238
            new_license = {
239
                "name": self.dialog.licensesTableWidget.item(row, 0).text,
240
                "file": self.dialog.licensesTableWidget.item(row, 1).text(),
241
            }
242
            licenses.append(new_license)
243
        self.metadata.License = licenses
244

245
        return self.dialog.addonKindComboBox.currentData(), self.metadata
246

247
    ###############################################################################################
248
    #                                         DIALOG SLOTS
249
    ###############################################################################################
250

251
    def _browse_for_icon_clicked(self):
252
        """Callback: when the "Browse..." button for the icon field is clicked"""
253
        subdir = self.dialog.subdirectoryLineEdit.text()
254
        start_dir = os.path.join(self.path_to_addon, subdir)
255
        new_icon_path, _ = QFileDialog.getOpenFileName(
256
            parent=self.dialog,
257
            caption=translate(
258
                "AddonsInstaller",
259
                "Select an icon file for this content item",
260
            ),
261
            dir=start_dir,
262
        )
263

264
        if not new_icon_path:
265
            return
266

267
        base_path = self.path_to_addon.replace("/", os.path.sep)
268
        icon_path = new_icon_path.replace("/", os.path.sep)
269
        if base_path[-1] != os.path.sep:
270
            base_path += os.path.sep
271

272
        if not icon_path.startswith(base_path):
273
            FreeCAD.Console.PrintError(
274
                translate("AddonsInstaller", "{} is not a subdirectory of {}").format(
275
                    icon_path, base_path
276
                )
277
                + "\n"
278
            )
279
            return
280
        self._set_icon(new_icon_path[len(base_path) :])
281
        self.metadata.Icon = new_icon_path[len(base_path) :]
282

283
    def _browse_for_subdirectory_clicked(self):
284
        """Callback: when the "Browse..." button for the subdirectory field is clicked"""
285
        subdir = self.dialog.subdirectoryLineEdit.text()
286
        start_dir = os.path.join(self.path_to_addon, subdir)
287
        new_subdir_path = QFileDialog.getExistingDirectory(
288
            parent=self.dialog,
289
            caption=translate(
290
                "AddonsInstaller",
291
                "Select the subdirectory for this content item",
292
            ),
293
            dir=start_dir,
294
        )
295
        if not new_subdir_path:
296
            return
297
        if new_subdir_path[-1] != "/":
298
            new_subdir_path += "/"
299

300
        # Three legal possibilities:
301
        #   1) This might be the toplevel directory, in which case we want to set
302
        #      metadata.Subdirectory to "./"
303
        #   2) This might be a subdirectory with the same name as the content item, in which case
304
        #      we don't need to set metadata.Subdirectory at all
305
        #   3) This might be some other directory name, but still contained within the top-level
306
        #      directory, in which case we want to set metadata.Subdirectory to the relative path
307

308
        # First, reject anything that isn't within the appropriate directory structure:
309
        base_path = self.path_to_addon.replace("/", os.path.sep)
310
        subdir_path = new_subdir_path.replace("/", os.path.sep)
311
        if not subdir_path.startswith(base_path):
312
            FreeCAD.Console.PrintError(
313
                translate("AddonsInstaller", "{} is not a subdirectory of {}").format(
314
                    subdir_path, base_path
315
                )
316
                + "\n"
317
            )
318
            return
319

320
        relative_path = subdir_path[len(base_path) :]
321
        if not relative_path:
322
            relative_path = "./"
323
        elif relative_path[-1] == os.path.sep:
324
            relative_path = relative_path[:-1]
325
        self.dialog.subdirectoryLineEdit.setText(relative_path)
326

327
    def _tags_clicked(self):
328
        """Show the tag editor"""
329
        tags = []
330
        if not self.metadata:
331
            self.metadata = FreeCAD.Metadata()
332
        if self.metadata:
333
            tags = self.metadata.Tag
334
        dlg = EditTags(tags)
335
        new_tags = dlg.exec()
336
        self.metadata.Tag = new_tags
337

338
    def _freecad_versions_clicked(self):
339
        """Show the FreeCAD version editor"""
340
        if not self.metadata:
341
            self.metadata = FreeCAD.Metadata()
342
        dlg = EditFreeCADVersions()
343
        dlg.exec(self.metadata)
344

345
    def _dependencies_clicked(self):
346
        """Show the dependencies editor"""
347
        if not self.metadata:
348
            self.metadata = FreeCAD.Metadata()
349
        dlg = EditDependencies()
350
        dlg.exec(self.metadata)  # Modifies metadata directly
351

352

353
class EditTags:
354
    """A dialog to edit tags"""
355

356
    def __init__(self, tags: List[str] = None):
357
        self.dialog = FreeCADGui.PySideUic.loadUi(
358
            os.path.join(os.path.dirname(__file__), "developer_mode_tags.ui")
359
        )
360
        self.original_tags = tags
361
        if tags:
362
            self.dialog.lineEdit.setText(", ".join(tags))
363

364
    def exec(self):
365
        """Execute the dialog, returning a list of tags (which may be empty, but still represents
366
        the expected list of tags to be set, e.g. the user may have removed them all).
367
        """
368
        result = self.dialog.exec()
369
        if result == QDialog.Accepted:
370
            new_tags: List[str] = self.dialog.lineEdit.text().split(",")
371
            clean_tags: List[str] = []
372
            for tag in new_tags:
373
                clean_tags.append(tag.strip())
374
            return clean_tags
375
        return self.original_tags
376

377

378
class EditDependencies:
379
    """A dialog to edit dependency information"""
380

381
    def __init__(self):
382
        self.dialog = FreeCADGui.PySideUic.loadUi(
383
            os.path.join(os.path.dirname(__file__), "developer_mode_dependencies.ui")
384
        )
385
        self.dialog.addDependencyToolButton.setIcon(
386
            QIcon.fromTheme("add", QIcon(":/icons/list-add.svg"))
387
        )
388
        self.dialog.removeDependencyToolButton.setIcon(
389
            QIcon.fromTheme("remove", QIcon(":/icons/list-remove.svg"))
390
        )
391
        self.dialog.addDependencyToolButton.clicked.connect(self._add_dependency_clicked)
392
        self.dialog.removeDependencyToolButton.clicked.connect(self._remove_dependency_clicked)
393
        self.dialog.tableWidget.itemDoubleClicked.connect(self._edit_dependency)
394
        self.dialog.tableWidget.itemSelectionChanged.connect(self._current_index_changed)
395

396
        self.dialog.removeDependencyToolButton.setDisabled(True)
397
        self.metadata = None
398

399
    def exec(self, metadata: FreeCAD.Metadata):
400
        """Execute the dialog"""
401
        self.metadata = FreeCAD.Metadata(metadata)  # Make a copy, in case we cancel
402
        row = 0
403
        for dep in self.metadata.Depend:
404
            dep_type = dep["type"]
405
            dep_name = dep["package"]
406
            dep_optional = dep["optional"]
407
            self._add_row(row, dep_type, dep_name, dep_optional)
408
            row += 1
409
        result = self.dialog.exec()
410
        if result == QDialog.Accepted:
411
            metadata.Depend = self.metadata.Depend
412

413
    def _add_dependency_clicked(self):
414
        """Callback: The add button was clicked"""
415
        dlg = EditDependency()
416
        dep_type, dep_name, dep_optional = dlg.exec()
417
        if dep_name:
418
            row = self.dialog.tableWidget.rowCount()
419
            self._add_row(row, dep_type, dep_name, dep_optional)
420
            self.metadata.addDepend(
421
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
422
            )
423

424
    def _add_row(self, row, dep_type, dep_name, dep_optional):
425
        """Utility function to add a row to the table."""
426
        translations = {
427
            "automatic": translate("AddonsInstaller", "Automatic"),
428
            "workbench": translate("AddonsInstaller", "Workbench"),
429
            "addon": translate("AddonsInstaller", "Addon"),
430
            "python": translate("AddonsInstaller", "Python"),
431
        }
432
        if dep_type and dep_name:
433
            self.dialog.tableWidget.insertRow(row)
434
            type_item = QTableWidgetItem(translations[dep_type])
435
            type_item.setData(Qt.UserRole, dep_type)
436
            self.dialog.tableWidget.setItem(row, 0, type_item)
437
            self.dialog.tableWidget.setItem(row, 1, QTableWidgetItem(dep_name))
438
            if dep_optional:
439
                self.dialog.tableWidget.setItem(
440
                    row, 2, QTableWidgetItem(translate("AddonsInstaller", "Yes"))
441
                )
442

443
    def _remove_dependency_clicked(self):
444
        """Callback: The remove button was clicked"""
445
        items = self.dialog.tableWidget.selectedItems()
446
        if items:
447
            row = items[0].row()
448
            dep_type = self.dialog.tableWidget.item(row, 0).data(Qt.UserRole)
449
            dep_name = self.dialog.tableWidget.item(row, 1).text()
450
            dep_optional = bool(self.dialog.tableWidget.item(row, 2))
451
            self.metadata.removeDepend(
452
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
453
            )
454
            self.dialog.tableWidget.removeRow(row)
455

456
    def _edit_dependency(self, item):
457
        """Callback: the dependency was double-clicked"""
458
        row = item.row()
459
        dlg = EditDependency()
460
        dep_type = self.dialog.tableWidget.item(row, 0).data(Qt.UserRole)
461
        dep_name = self.dialog.tableWidget.item(row, 1).text()
462
        dep_optional = bool(self.dialog.tableWidget.item(row, 2))
463
        new_dep_type, new_dep_name, new_dep_optional = dlg.exec(dep_type, dep_name, dep_optional)
464
        if dep_type and dep_name:
465
            self.metadata.removeDepend(
466
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
467
            )
468
            self.metadata.addDepend(
469
                {
470
                    "package": new_dep_name,
471
                    "type": new_dep_type,
472
                    "optional": new_dep_optional,
473
                }
474
            )
475
            self.dialog.tableWidget.removeRow(row)
476
            self._add_row(row, dep_type, dep_name, dep_optional)
477

478
    def _current_index_changed(self):
479
        if self.dialog.tableWidget.selectedItems():
480
            self.dialog.removeDependencyToolButton.setDisabled(False)
481
        else:
482
            self.dialog.removeDependencyToolButton.setDisabled(True)
483

484

485
class EditDependency:
486
    """A dialog to edit a single piece of dependency information"""
487

488
    def __init__(self):
489
        self.dialog = FreeCADGui.PySideUic.loadUi(
490
            os.path.join(os.path.dirname(__file__), "developer_mode_edit_dependency.ui")
491
        )
492

493
        self.dialog.typeComboBox.addItem(
494
            translate("AddonsInstaller", "Internal Workbench"), "workbench"
495
        )
496
        self.dialog.typeComboBox.addItem(translate("AddonsInstaller", "External Addon"), "addon")
497
        self.dialog.typeComboBox.addItem(translate("AddonsInstaller", "Python Package"), "python")
498

499
        self.dialog.typeComboBox.currentIndexChanged.connect(self._type_selection_changed)
500
        self.dialog.dependencyComboBox.currentIndexChanged.connect(
501
            self._dependency_selection_changed
502
        )
503

504
        # Expect mostly Python dependencies...
505
        self.dialog.typeComboBox.setCurrentIndex(2)
506

507
        self.dialog.layout().setSizeConstraint(QLayout.SetFixedSize)
508

509
    def exec(self, dep_type="", dep_name="", dep_optional=False) -> Tuple[str, str, bool]:
510
        """Execute the dialog, returning a tuple of the type of dependency (workbench, addon, or
511
        python), the name of the dependency, and a boolean indicating whether this is optional.
512
        """
513

514
        # If we are editing an existing row, set up the dialog:
515
        if dep_type and dep_name:
516
            index = self.dialog.typeComboBox.findData(dep_type)
517
            if index == -1:
518
                raise RuntimeError(f"Invalid dependency type {dep_type}")
519
            self.dialog.typeComboBox.setCurrentIndex(index)
520
            index = self.dialog.dependencyComboBox.findData(dep_name)
521
            if index == -1:
522
                index = self.dialog.dependencyComboBox.findData("other")
523
            self.dialog.dependencyComboBox.setCurrentIndex(index)
524
            self.dialog.lineEdit.setText(dep_name)
525
            self.dialog.optionalCheckBox.setChecked(dep_optional)
526

527
        # Run the dialog (modal)
528
        result = self.dialog.exec()
529
        if result == QDialog.Accepted:
530
            dep_type = self.dialog.typeComboBox.currentData()
531
            dep_optional = self.dialog.optionalCheckBox.isChecked()
532
            dep_name = self.dialog.dependencyComboBox.currentData()
533
            if dep_name == "other":
534
                dep_name = self.dialog.lineEdit.text()
535
            return dep_type, dep_name, dep_optional
536
        return "", "", False
537

538
    def _populate_internal_workbenches(self):
539
        """Add all known internal FreeCAD Workbenches to the list"""
540
        self.dialog.dependencyComboBox.clear()
541
        for display_name, name in INTERNAL_WORKBENCHES.items():
542
            self.dialog.dependencyComboBox.addItem(display_name, name)
543
        # No "other" option is supported for this type of dependency
544

545
    def _populate_external_addons(self):
546
        """Add all known addons to the list"""
547
        self.dialog.dependencyComboBox.clear()
548
        # pylint: disable=import-outside-toplevel
549
        from AddonManager import INSTANCE as AM_INSTANCE
550

551
        repo_dict = {}
552
        # We need a case-insensitive sorting of all repo types, displayed and sorted by their
553
        # display name, but keeping track of their official name as well (stored in the UserRole)
554
        for repo in AM_INSTANCE.item_model.repos:
555
            repo_dict[repo.display_name.lower()] = (repo.display_name, repo.name)
556
        sorted_keys = sorted(repo_dict)
557
        for item in sorted_keys:
558
            self.dialog.dependencyComboBox.addItem(repo_dict[item][0], repo_dict[item][1])
559
        self.dialog.dependencyComboBox.addItem(translate("AddonsInstaller", "Other..."), "other")
560

561
    def _populate_allowed_python_packages(self):
562
        """Add all allowed python packages to the list"""
563
        self.dialog.dependencyComboBox.clear()
564
        # pylint: disable=import-outside-toplevel
565
        from AddonManager import INSTANCE as AM_INSTANCE
566

567
        packages = sorted(AM_INSTANCE.allowed_packages)
568
        for package in packages:
569
            self.dialog.dependencyComboBox.addItem(package, package)
570
        self.dialog.dependencyComboBox.addItem(translate("AddonsInstaller", "Other..."), "other")
571

572
    def _type_selection_changed(self, _):
573
        """Callback: The type of dependency has been changed"""
574
        selection = self.dialog.typeComboBox.currentData()
575
        if selection == "workbench":
576
            self._populate_internal_workbenches()
577
        elif selection == "addon":
578
            self._populate_external_addons()
579
        elif selection == "python":
580
            self._populate_allowed_python_packages()
581
        else:
582
            raise RuntimeError("Invalid data found for selection")
583

584
    def _dependency_selection_changed(self, _):
585
        selection = self.dialog.dependencyComboBox.currentData()
586
        if selection == "other":
587
            self.dialog.lineEdit.show()
588
            self.dialog.otherNote.show()
589
        else:
590
            self.dialog.lineEdit.hide()
591
            self.dialog.otherNote.hide()
592

593

594
class EditFreeCADVersions:
595
    """A dialog to edit minimum and maximum FreeCAD version support"""
596

597
    def __init__(self):
598
        self.dialog = FreeCADGui.PySideUic.loadUi(
599
            os.path.join(os.path.dirname(__file__), "developer_mode_freecad_versions.ui")
600
        )
601

602
    def exec(self, metadata: FreeCAD.Metadata):
603
        """Execute the dialog"""
604
        if metadata.FreeCADMin != "0.0.0":
605
            self.dialog.minVersionLineEdit.setText(metadata.FreeCADMin)
606
        if metadata.FreeCADMax != "0.0.0":
607
            self.dialog.maxVersionLineEdit.setText(metadata.FreeCADMax)
608
        result = self.dialog.exec()
609
        if result == QDialog.Accepted:
610
            if self.dialog.minVersionLineEdit.text():
611
                metadata.FreeCADMin = self.dialog.minVersionLineEdit.text()
612
            else:
613
                metadata.FreeCADMin = None
614
            if self.dialog.maxVersionLineEdit.text():
615
                metadata.FreeCADMax = self.dialog.maxVersionLineEdit.text()
616
            else:
617
                metadata.FreeCADMax = None
618

619

620
class EditAdvancedVersions:
621
    """A dialog to support mapping specific git branches, tags, or commits to specific
622
    versions of FreeCAD."""
623

624
    def __init__(self):
625
        self.dialog = FreeCADGui.PySideUic.loadUi(
626
            os.path.join(os.path.dirname(__file__), "developer_mode_advanced_freecad_versions.ui")
627
        )
628

629
    def exec(self):
630
        """Execute the dialog"""
631
        self.dialog.exec()
632

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.