Macro Packages/fr

Other languages:

Macro Packages

Description
Interface graphique pour pip pour l'installation et la suppression des paquets pypi dans l'environnement Python de FreeCAD.

Version macro : 0.4
Date dernière modification : 2025.01.18
Version FreeCAD : 1.0
Téléchargement : Icône de la barre d'outils
Auteur: TheMarkster
Auteur
TheMarkster
Téléchargement
Icône de la barre d'outils
Liens
Version Macro
0.4
Dernière modification
2025.01.18
Version(s) FreeCAD
1.0
Raccourci clavier
None
Voir aussi
None

Description

Il s'agit d'une interface graphique pour pip qui permet d'installer des paquets pypi dans l'environnement Python de FreeCAD.

Capture d'écran de Packages

Utilisation

Cette macro installera les paquets pypi dans un dossier spécial appelé AdditionalPythonPackages. Il s'agit exactement du même dossier que celui utilisé par le gestionnaire d'extensions pour installer les dépendances de diverses extensions. Les fichiers de ce dossier peuvent avoir été installés par le gestionnaire d'extensions ou par cette macro. Vous pouvez cliquer sur le bouton dans le coin inférieur droit pour ouvrir l'emplacement du dossier dans votre navigateur de fichiers par défaut. Notez que si le dossier n'existe pas encore, il sera créé pour vous. Si le dossier doit être créé, FreeCAD ne reconnaîtra pas les paquets qui y sont installés jusqu'à ce que FreeCAD soit redémarré. Après cela, aucun redémarrage de FreeCAD ne devrait être nécessaire. Les paquets sont installés à l'aide de pip. Ceux-ci sont installés à partir de pypi. Seuls les 8000 paquets les plus téléchargés sont listés, mais vous pouvez utiliser le bouton de recherche pour installer ceux qui ne font pas partie des 8000 paquets les plus populaires. Le bouton Appliquer le filtre utilise le contenu de l'édition de la ligne de filtre pour filtrer les paquets de la liste des 8 000 premiers. La case à cocher Trier permet d'alterner entre le tri alphabétique et le tri basé sur la popularité (par défaut, la case n'est pas cochée). Le bouton ouvrir l'url ouvre la page pypi pour le paquet sélectionné dans votre navigateur par défaut.

Notez qu'un paquet peut avoir des dépendances supplémentaires, qui seront également installées, et lorsque vous supprimez le paquet, les dépendances qui ont été installées avec lui seront également supprimées, à moins qu'elles n'aient été installées en tant que dépendances d'un autre paquet. Il est possible d'effacer le dossier entier pour supprimer toutes les dépendances installées avec cette macro et avec le gestionnaire d'extensions. Mais il est évident que cela risque de casser les extensions qui ont besoin de ces paquets.

Les paquets installés seront placés dans un sous-dossier basé sur la version de Python utilisée. Si FreeCAD est mis à jour vers une version plus récente de Python, alors ces paquets devront très probablement être réinstallés. Vous pouvez simplement les copier dans le dossier de la nouvelle version de Python, mais il est préférable d'utiliser pip au cas où la nouvelle version de Python nécessiterait une version plus récente du paquet.

Script

Icône de la barre d'outils

packages.py

# packages.py
# 2024, <TheMarkster> LGPL 2.1 or later
# thanks to forum member hasecilu for help with setting pyside6 icons
# a utility to install Python packages in FreeCAD

__version__ = "0.4"

from PySide import QtWidgets, QtGui, QtCore
QDialog = QtWidgets.QDialog
QVBoxLayout = QtWidgets.QVBoxLayout
QHBoxLayout = QtWidgets.QHBoxLayout
QLabel = QtWidgets.QLabel
QLineEdit = QtWidgets.QLineEdit
QSpinBox = QtWidgets.QSpinBox
QPushButton = QtWidgets.QPushButton
QListWidget = QtWidgets.QListWidget
QMessageBox = QtWidgets.QMessageBox
QCheckBox = QtWidgets.QCheckBox
QApplication = QtGui.QApplication
QFileDialog = QtGui.QFileDialog
Qt = QtCore.Qt

import requests
import re
import webbrowser
import subprocess, sys, platform
import freecad.utils as utils
import addonmanager_utilities as amutils
import shutil
import json
import importlib

class Packages:
    def __init__(self):
        self.python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
        self.pg = FreeCAD.ParamGet(f"User parameter:Plugins/Packages_Macro/{self.python_version}")
        layout = QVBoxLayout()
        self.mw = FreeCADGui.getMainWindow()
        self.pathEdit = QLineEdit()
        self.pathEdit.setToolTip("Path to python executable.")
        self.pathEdit.setText(self.findExecutable())
        self.form = QtWidgets.QWidget()
        self.form.setLayout(layout)
        self.mode = "install" # can also be "uninstall"
        filterLayout = QHBoxLayout()
        self.filterEdit = QLineEdit()
        self.filterEdit.setPlaceholderText("Filter packages")
        self.filterEdit.returnPressed.connect(self.applyFilter)
        self.filterEdit.textChanged.connect(self.enableButtons)
        filterLayout.addWidget(QLabel("Filter:"))
        filterLayout.addWidget(self.filterEdit)
        self.sortedCheckBox = QCheckBox("Sort")
        self.sortedCheckBox.setToolTip("""
Default is sorting by popularity in terms of most downloads in the last 30 days.
Checked = sort A to Z.
Note toggling applies the filter.""")
        self.sortedCheckBox.setChecked(False)
        self.sortedCheckBox.stateChanged.connect(self.applyFilter)
        # this is to initialize stateChanged, otherwise first toggle does not update
        checked = self.sortedCheckBox.isChecked()
        filterLayout.addWidget(self.sortedCheckBox)
        
        self.filterButton = QPushButton("Apply Filter")
        self.filterButton.clicked.connect(self.applyFilter)
        filterLayout.addWidget(self.filterButton)
        
        self.searchButton = QPushButton("Search")
        self.searchButton.setIcon(FreeCADGui.getIcon("internet-web-browser"))
        self.searchButton.setToolTip("Search for filter on pypi.org in default browser")
        self.searchButton.setEnabled(False)
        filterLayout.addWidget(self.searchButton)
        self.searchButton.clicked.connect(self.search)
        
        self.packageList = QListWidget()
        self.packageList.itemSelectionChanged.connect(self.enableButtons)

        self.fetchButton = QPushButton("Fetch summary")
        self.fetchButton.setIcon(FreeCADGui.getIcon("internet-web-browser"))
        self.fetchButton.setEnabled(False)
        self.fetchButton.clicked.connect(self.fetchSummary)

        layout.addLayout(filterLayout)

        layout.addWidget(self.packageList)
        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.fetchButton)
        
        self.installButton = QPushButton("Install / Update")
        self.installButton.clicked.connect(self.install)
        self.installButton.setEnabled(False)
        self.installButton.setToolTip("""
If this is disabled with an active selection it means the package is a base package.
You are allowed to install again an added package because there might be an update
available for it.""")
        buttonLayout.addWidget(self.installButton)
        
        self.uninstallButton = QPushButton("Uninstall")
        style = self.form.style()
        try:
            trashIcon = style.standardIcon(style.SP_TrashIcon)
        except:
            trashIcon = style.standardIcon(style.StandardPixmap.SP_TrashIcon)
        self.uninstallButton.setIcon(trashIcon)
        self.uninstallButton.clicked.connect(self.uninstall)
        self.uninstallButton.setEnabled(False)
        self.uninstallButton.setToolTip("""
If this is disabled with an active selection it means the selected package is
either not already installed or it is a base package included with the packaging
or distribution.""")
        buttonLayout.addWidget(self.uninstallButton)
        
        self.openUrlButton = QPushButton("Open Url")
        self.openUrlButton.setIcon(FreeCADGui.getIcon("internet-web-browser"))
        self.openUrlButton.clicked.connect(self.openUrl)
        self.openUrlButton.setEnabled(False)
        buttonLayout.addWidget(self.openUrlButton)
        
        layout.addLayout(buttonLayout)
        
        pathLayout = QHBoxLayout()
        layout.addLayout(pathLayout)

        self.pathEdit.setPlaceholderText("Path to python executable")
        pathLayout.addWidget(QLabel("Python:"))
        pathLayout.addWidget(self.pathEdit)
        self.pathButton = QPushButton("...")
        try:
            self.pathButton.setIcon(style.standardIcon(style.SP_FileDialogStart))
        except:
            self.pathButton.setIcon(style.standardIcon(style.StandardPixmap.SP_FileDialogStart))
        self.pathButton.setFixedWidth(self.pathButton.sizeHint().width())
        self.pathButton.setToolTip("Click to browse for python executable.")
        self.pathButton.clicked.connect(self.setupPath)
        pathLayout.addWidget(self.pathButton)
        
        additionalLayout = QHBoxLayout()
        layout.addLayout(additionalLayout)
        additionalLayout.addWidget(QLabel("AdditionalPythonPackages folder:"))
        self.additionalEdit = QLineEdit()
        self.additionalEdit.setPlaceholderText("AdditionalPythonPackages folder goes here")
        pathToPackages = self.pg.GetString("PathToPackages","")
        if not pathToPackages:
            pathToPackages = amutils.get_pip_target_directory()
            if not os.path.exists(pathToPackages):
                os.makedirs(pathToPackages)
                self.pg.SetString("PathToPackages", pathToPackages)
                FreeCAD.Console.PrintMessage("AdditionalPythonPackages folder has been created.  You will likely need to restart FreeCAD to use any newly installed packages.\n")
        self.additionalEdit.setText(pathToPackages)
        self.additionalEdit.setReadOnly(True)
        additionalLayout.addWidget(self.additionalEdit)
        self.additionalButton = QPushButton("Open")
        try:
            self.additionalButton.setIcon(style.standardIcon(style.SP_DirOpenIcon))
        except:
            self.additionalButton.setIcon(style.standardIcon(style.StandardPixmap.SP_DirOpenIcon))
        self.additionalButton.clicked.connect(self.openAdditional)
        self.additionalButton.setToolTip("""
Opens the AdditionalPythonPackages folder.  Note that the only things
that should be in this folder are things installed using this macro
or additional dependencies installed via the addon manager.  It should
be relatively safe to remove things from here.  You should take note
of what extra folders were added when you install something.  These
dependencies are not being tracked as pip doesn't support tracking
packages added to custom folders.  When we uninstall something we
only install what we added, not the additional dependencies pip might
have added.""")
        additionalLayout.addWidget(self.additionalButton)
        
        self.packages = self.loadPackageData()
        title = f"Packages {__version__} ({len(self.packages)} packages)"
        self.form.setWindowTitle(title)
        self.applyFilter()

        self.form.setWindowIcon(self.QIconFromXPMString(__icon__))

    def loadPackageData(self):
        # would be faster to cache this locally, but this way it's always up to date
        url = "https://hugovk.github.io/top-pypi-packages/top-pypi-packages-30-days.json"
        try:
            response = requests.get(url)
            response.raise_for_status()
            data = response.json()
            return data["rows"]
        except requests.RequestException as e:
            QMessageBox.critical(self.mw, "Error", f"Could not load data: {e}")
            return []
            
    def openAdditional(self):
        """
        open the addtional python packages folder.  note that this is a different
        folder for each version of python used by various freecad installations, 
        for example if the user has multiple versions in simultaneous use.  this
        folder is really for the addon manager to install dependencies when installing
        various addons, so we do not have exclusive use of it.  but the advantage
        is the addon manager can also update these for use when updates are available.
        this folder is made available to python via initGui,py in src/App, which
        is how addons are able to import the packages installed here.
        """
        sys = platform.system()
        userFolder = self.additionalEdit.text()
        if 'Windows' in sys:
            subprocess.Popen('start explorer.exe '+userFolder, shell=True)
        elif 'Linux' in sys:
            os.system("xdg-open '%s'" % userFolder)
        elif 'Darwin' in sys:
            subprocess.Popen(["open", userFolder])
        else:
            msgBox = QtGui.QMessageBox()
            msg = "We were unable to determine your platform, and thus cannot open your '+userFolder+' for you, but you can still do it manually.\n"
            msgBox.exec_()
                    
    def getStandardButtons(self):
        return (QtWidgets.QDialogButtonBox.Close)
        
    def setupPath(self):
        """
        This is for the user to bre able to set a python that he wants to use
        rather than the default found with the freecad.utils function
        """
        path, ok = QFileDialog.getOpenFileName(self.mw, 
                "Select Python Executable",
                "",
                "Python Executable (python*);;All Files (*)")
        if path:
            self.pathEdit.setText(path)
            self.pg.SetString("PathToPython", path)


    def applyFilter(self):
        """
        This is called when the user clicks the Apply filters button, and also
        when the user toggles the sort checkbox, and when the enter key is pressed
        in the filter line edit widget.  If sorting is active, then the filtered
        packages are sorted alphabetically, otherwise they are sorted based on the 
        popularity as measured by the number of downloads in the last 30 days.
        """
        filter = self.filterEdit.text()
        try:
            filtered = [pkg for pkg in self.packages if re.search(filter, pkg["project"], re.IGNORECASE)]
            exactMatch = False
            self.packageList.clear()
            for pkg in filtered:
                self.packageList.addItem(pkg["project"])
                if pkg == filter:
                    exactMatch = True
            self.packageList.setSortingEnabled(self.sortedCheckBox.isChecked())
            if filter and not exactMatch:
                description, version = self.getInfo(filter)
                if description:
                    self.packageList.insertItem(0, filter + " (not in top 8000)")

        except re.error as e:
            QMessageBox.warning(self.mw, "Invalid Regex", f"Error in regular expression: {e}")
            self.form.setWindowTitle(f"Packages {__version__}")

    def enableButtons(self):
        """enables or disables buttons based on whether a package is selected
        in the list widget and whether that package is already installed."""
        hasSelection = bool(self.packageList.selectedItems())
        self.openUrlButton.setEnabled(hasSelection)
        self.fetchButton.setEnabled(hasSelection)
        hasFilter = bool(self.filterEdit.text())
        self.searchButton.setEnabled(hasFilter)
            
        added, base = (False, False)
        if hasSelection:
            added, base = self.isInstalled()
        self.installButton.setEnabled(not base)
        self.uninstallButton.setEnabled(added)

    def install(self):
        self.mode = "install"
        self.managePackages()
        
    def uninstall(self):
        self.mode = "uninstall"
        self.managePackages()
        
    def findExecutable(self):
        """
        find the python executable in use, luckily there is a function for this
        already in FreeCAD, but we still want to allow the user the flexibility
        of setting a different python executable if the function gets it wrong
        """
        
        path = self.pg.GetString("PathToPython","")
        if path:
            return path
        
        pyEx = utils.get_python_exe()
    
        if os.path.exists(pyEx):
            self.pg.SetString("PathToPython", pyEx)
            return pyEx
        else:
            FreeCAD.Console.PrintError(f"Python executable not found.  You may try to find it manually by clicking the '...' button at the bottom of the dialog.\n")
            return "Path to python executable"

    def managePackages(self):
        """
        Here is where the installing and uninstalling happens.  We track which
        packages we have installed and which dependencies were installed along
        with them.  The caveat here is the AddonManager might also install stuff
        in our folder, but I don't think the AM tracks extra dependencies.
        """
        package = self.packageList.currentItem().text().replace(" (not in top 8000)", "")
        python_exe = self.pathEdit.text()
        vendor_path = self.additionalEdit.text()
        if "APPIMAGE" in os.environ:
            
            os.environ['PYTHONHOME'] = '/usr'
            os.environ['PYTHONPATH'] = f"/usr/lib/python{self.python_version}/site-packages" 

        if self.mode == "uninstall":
            added = self.loadAddedSubfolders(package)
            self.pg.RemString(f"{package}_Subfolders")

            for subfolder in added:
                item_path = os.path.join(vendor_path, subfolder)
                if os.path.exists(item_path):
                    shutil.rmtree(item_path)
                    print(f"{subfolder} removed\n")

            self.enableButtons()
            return

        existing = set(os.listdir(vendor_path))

        command = [python_exe, "-m", "pip", self.mode, "--target", vendor_path, package]

        try:
            result = subprocess.run(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                check=True
            )
            FreeCAD.Console.PrintMessage(f"{result.stdout}\n")


            new_folders = set(os.listdir(vendor_path)) - existing
            added = list(new_folders)

            self.saveAddedSubfolders(package, added)

        except subprocess.CalledProcessError as e:
            FreeCAD.Console.PrintError(f"Error: {e.stderr}\n")
        except Exception as e:
            FreeCAD.Console.PrintError(f"An unexpected error occurred: {e}\n")
        finally:
            self.enableButtons()

    def saveAddedSubfolders(self, package, added):
        """
        Here we track the folders that we added to this AdditionalPythonPackages
        folder and removed them during uninstallation.  The complication is that
        sometimes additional dependencies are also added, and when they are, we 
        should also track them and remove them.  Further complicating this is other
        packages might have also added the same dependencies.  These we reconcile
        in the loadAddedSubfolders() method below.
        """
        json_string = json.dumps(added)
        self.pg.SetString(f"{package}_Subfolders", json_string)

    def loadAddedSubfolders(self, package):
        """
        We get all of the packages that were added by all installations, this is
        in allPackages list.  The added list only includes the new subfolders that
        were added when this package was installed.  Anything in added that is not
        also in allPackages can be safely removed during uninstallation.
        """
        json_string = self.pg.GetString(f"{package}_Subfolders", "")
        added = json.loads(json_string) if json_string else []

        allPackages = []
        for key in self.pg.GetStrings():
            if key.endswith("_Subfolders") and key != f"{package}_Subfolders":
                other_json = self.pg.GetString(key, "")
                try:
                    otherPackages = json.loads(other_json)
                    allPackages.extend(otherPackages)
                except json.JSONDecodeError:
                    print(f"Error decoding subfolder list for {key}.")

        unique = [folder for folder in added if folder not in allPackages]
        return unique

    def isInstalled(self):
        """
        Called only by the enable buttons function.  We return a tuple (added, base)
        added = True means this was something we added to the additional packages 
        folder.  base = True means this is installed, but as a base package either
        through the packaging or user installed into the system environment, either
        way we don't try to install or uninstall anything we didn't add or that the
        addon manager didn't add as an additional dependency in our user addon folder
        """
        added = False
        base = False
        package = self.packageList.currentItem().text().replace(" (not in top 8000)", "")
        python_exe = self.pathEdit.text()
        vendor_path = amutils.get_pip_target_directory()
        if not os.path.exists(vendor_path):
            os.makedirs(vendor_path)
        fullpath = os.path.join(vendor_path,package)
        if os.path.exists(fullpath):
            added = True
        try:
            importlib.import_module(package)
            if not added: # we didn't add this
                base = True
        except ImportError:
            # added might or might not be false because this might not yet be
            # available to import if the additionalpythonpackages folder was
            # only just created this FreeCAD session, so we don't set it False
            # here, but instead rely on the above test
            base = False

        return (added, base)

    def fetchSummary(self):
        selectedItem = self.packageList.currentItem()
        if not selectedItem:
            return
        
        package = selectedItem.text().replace(" (not in top 8000)","")
        description, version = self.getInfo(package)
        
        QMessageBox.information(self.mw, "Package Summary", 
                                f"{package} (v{version}): {description}")
        

    def getInfo(self, package):
        """
        called by fetchSummary(), pops up a very brief description of the package
        in a message box.  User can use the OpenUrl button to get more details.
        We also use this to check if a custom entered package is available outside
        the top 8000 list.
        """
        
        url = f"https://pypi.org/pypi/{package}/json"
        try:
            response = requests.get(url)
            response.raise_for_status()
            data = response.json()
            description = data["info"].get("summary", "No description available.")
            version = data["info"].get("version", "Unknown version")
            return description, version
        except requests.RequestException:
            return "", "Unknown version"

    def search(self):
        filter = self.filterEdit.text()
        if not filter:
            return
        url = f"https://pypi.org/search/?q={filter}"
        try:
            webbrowser.open(url)
        except Exception as e:
            QMessageBox.critical(self.mw, "Error", 
                                f"Unable to open the browser. Error: {e}")

    def openUrl(self):
        package = self.packageList.currentItem().text().replace(" (not in top 8000)", "")
        url = f"https://pypi.org/pypi/{package}"
        try:
            webbrowser.open(url)
        except Exception as e:
            QMessageBox.critical(self.mw, "Error", 
                                f"Unable to open the browser. Error: {e}")

    def QIconFromXPMString(self, xpm_string):
        if "/*pixels*/" in xpm_string:
            xpm = xpm_string.replace("\"","").replace(',','').splitlines()[4:]
        else:
            xpm = xpm_string.replace("\"","").replace(',','').splitlines()[3:]
        for line in reversed(xpm):
            if line.startswith("/*") and line.endswith("*/"):
                xpm.pop(xpm.index(line))
        pixmap = QtGui.QPixmap(xpm)
        icon = QtGui.QIcon(pixmap)
        return icon


__icon__ = """
/* XPM */
static char *dummy[]={
"64 64 1050 2",
"Qt c None",
".g c #010101",
"br c #020202",
"a3 c #030202",
"h3 c #030303",
"ej c #030304",
".I c #040404",
"ao c #050504",
".# c #050505",
"m9 c #060605",
"dI c #060606",
".J c #070707",
"a2 c #090907",
"nl c #090909",
"mW c #0a0a0a",
"mV c #0b0a09",
"aL c #0b0b09",
"bh c #0b0b0b",
".s c #0b0c0c",
"nz c #0c0b0b",
"oj c #0c0c0c",
"d3 c #0c0c0d",
"n9 c #0d0c0c",
"dJ c #0d0d0d",
"#b c #0d0d0e",
".W c #0d0e0f",
"mL c #0e0e0f",
"eI c #0f0f10",
"#T c #0f1317",
"#x c #0f1417",
"mK c #100f0d",
"nN c #11100e",
"nk c #11100f",
"mA c #111111",
"bM c #111112",
"#a c #111417",
"c# c #121212",
"#U c #121415",
"mo c #131313",
".t c #131315",
"mU c #15130e",
"nA c #151516",
"aK c #16140d",
"#y c #16181b",
"mM c #171717",
"fj c #181819",
".X c #181a1c",
"gY c #191919",
".f c #191b1d",
"h# c #1a1a1b",
"#7 c #1a1e21",
"m8 c #1c1a15",
"an c #1d1b15",
"oi c #1d1d1e",
"ot c #1e1e1e",
"mX c #202020",
"ap c #212328",
"aM c #212428",
"nY c #22211c",
"bs c #222223",
"h4 c #232325",
"#8 c #23252a",
"#c c #23272e",
"cx c #242425",
".H c #242629",
"#z c #242b34",
"o. c #252526",
".h c #25272a",
"ek c #262627",
".K c #262a2f",
"cP c #282729",
"d# c #282829",
"iM c #29292a",
"du c #29292b",
"#d c #2c3845",
"#A c #2e3d4b",
"am c #2f2919",
"aJ c #2f2a1c",
"m7 c #2f2a1d",
"nO c #2f2f30",
".A c #2f6491",
"mY c #303030",
".B c #306491",
".Q c #306591",
"ib c #306592",
"nj c #312d23",
"ny c #312f2a",
".Y c #313942",
"#E c #316591",
"eS c #316592",
"ja c #323233",
"a5 c #326592",
".z c #326692",
"mT c #332e21",
"av c #336692",
"#p c #336792",
"dC c #336793",
"a6 c #346792",
".R c #346793",
"aw c #346893",
"n. c #353536",
".Z c #354658",
".l c #35658f",
".m c #356792",
".6 c #356893",
"ev c #356894",
"os c #363637",
".u c #363d46",
".P c #366792",
"#i c #366894",
"dW c #366994",
"bg c #373630",
"mz c #373737",
"#e c #374d61",
"#h c #376893",
"f. c #376994",
"ax c #376a95",
"#R c #3775a9",
".y c #386891",
".n c #386993",
"ac c #386994",
"ew c #386a95",
"ba c #3875a9",
"bA c #3876a9",
"dl c #3876aa",
".o c #39678f",
"lj c #3976a9",
"c2 c #3977aa",
"nm c #3a3a3c",
".x c #3a648a",
"ab c #3a6a94",
".C c #3a6b95",
"dB c #3a6b96",
"aR c #3a6c96",
"#Q c #3a77aa",
"#Z c #3b6c96",
"ad c #3b6c97",
"ea c #3b6d96",
"b# c #3b77aa",
"dK c #3c3b3d",
".a c #3c4149",
".0 c #3c5974",
"#Y c #3c6c94",
"iw c #3c6c96",
"aC c #3c78aa",
"ht c #3c78ab",
".k c #3d6589",
".O c #3d688f",
"#D c #3d6b94",
"fw c #3d6d95",
"b. c #3d78aa",
"k2 c #3d79ab",
"bi c #3e3e40",
".D c #3e6c93",
"fr c #3e6c95",
"eT c #3e6e98",
"ae c #3e6f9a",
"dX c #3e79ab",
"iq c #3f3f42",
".w c #3f607d",
"g# c #3f6c95",
"gm c #3f6e96",
".7 c #3f6e98",
"#F c #3f6f98",
"lE c #3f79ab",
"#5 c #3f7aab",
"cH c #3f7aac",
"nM c #403c30",
"gQ c #406e96",
"au c #406e97",
"g4 c #406f96",
"#o c #406f98",
"cI c #407aab",
"fY c #407aac",
"#V c #414b56",
"aq c #415c76",
".E c #416789",
"ds c #417bab",
"#v c #417bac",
".r c #42474d",
"#B c #425a73",
"a4 c #425c75",
"aN c #425c76",
".S c #426f97",
"ay c #42729b",
"dD c #427bac",
"b0 c #427dae",
"jb c #434343",
"mN c #434344",
"#g c #436b91",
"kk c #437199",
"ic c #43719a",
"hr c #43729a",
"hV c #437cac",
"iN c #444446",
"#f c #446482",
"fs c #446f96",
"hd c #447299",
"ia c #44729a",
"aV c #447dad",
"ok c #454547",
"bN c #454548",
".v c #45576a",
"fP c #456f97",
"fx c #45739b",
"iB c #457dad",
".i c #465161",
".T c #466e91",
"hf c #467dad",
"n8 c #474541",
"a7 c #477198",
"ft c #477298",
"e9 c #477299",
"k1 c #47739b",
"eh c #477ead",
"eZ c #477eae",
"cn c #4780b0",
"ar c #487197",
"fR c #487298",
"aa c #487398",
"e6 c #487399",
"dV c #48759c",
"dE c #487fae",
"nx c #49422f",
"#C c #496c8e",
"fQ c #497398",
"iU c #49749a",
"eu c #49759b",
"gn c #4979a2",
"c3 c #497fae",
"bV c #4980ad",
"nB c #4a4a4c",
"fS c #4a7399",
"j8 c #4a79a2",
"g5 c #4a7aa3",
"bz c #4a80ad",
"go c #4a80ae",
"eY c #4a80af",
".j c #4b647d",
".N c #4b6b87",
"eR c #4b769b",
"cp c #4b80ae",
"bm c #4b81ae",
"fu c #4c7398",
"fv c #4c7499",
"#M c #4c7ba2",
"gS c #4c80aa",
"#P c #4c82af",
"nn c #4d4d4f",
"d4 c #4d4d50",
"e7 c #4d7499",
"#j c #4d789e",
"#q c #4d799f",
"gC c #4d81ad",
"bD c #4d84b2",
"ca c #4e4e4f",
"ex c #4e789e",
"kl c #4e7ea6",
"hs c #4e7ea8",
"i2 c #4e83af",
"he c #4f7ea7",
"n# c #504f51",
".1 c #507495",
"aQ c #50789c",
".5 c #507a9f",
"#L c #507ca2",
"by c #507fa6",
"id c #5083b0",
"lF c #5083b1",
"ci c #5084b0",
"dk c #5086b4",
"#9 c #51667b",
"hU c #517a9f",
"ee c #517ea6",
"ch c #517fa6",
"dY c #5184b0",
"fX c #5184b1",
"aE c #5185b1",
"aO c #527699",
"e8 c #52779a",
"eA c #527ba0",
"af c #527ea3",
"#s c #5280a8",
"c8 c #5284b0",
"fB c #5285b0",
"aj c #5285b1",
"bF c #5285b2",
"nZ c #535355",
".L c #535c67",
".F c #536b81",
".p c #537390",
"#X c #537697",
"a. c #53789a",
"e# c #53799b",
"h9 c #537b9f",
"eU c #537ca0",
"#J c #537ca1",
"fA c #5383ad",
"fc c #5383ae",
"k3 c #5385b1",
"do c #5386b1",
"l2 c #5386b2",
"ni c #544e3b",
"ga c #54799c",
"iX c #547ca0",
"fT c #547ca1",
"f# c #547ea2",
"#r c #5480a6",
"fV c #5482aa",
"cJ c #5485b1",
"aB c #5487b1",
"#I c #557da1",
"fU c #5581a7",
"eF c #5586b2",
"cY c #5588b3",
"iz c #567da1",
"#K c #567ea1",
"bl c #5681a7",
"a9 c #5686af",
"c4 c #5687b2",
".G c #57626e",
"at c #577b9d",
"eB c #5783a8",
"#u c #5788b2",
"al c #58543e",
".b c #586a80",
"a# c #587b9c",
"ix c #587da1",
"#m c #5880a3",
".8 c #5881a6",
"fb c #5886ad",
"fW c #5887b0",
"bB c #588cb7",
"iA c #597fa2",
"iy c #597fa3",
"#l c #5980a3",
".9 c #5984a9",
"fz c #5985ab",
"cD c #598ab4",
"c1 c #598cb7",
"jf c #5a7fa2",
"#t c #5a88b0",
"iC c #5a89b3",
"i# c #5b80a4",
".2 c #5b81a2",
"gp c #5b89ae",
"#4 c #5b8ab3",
"eJ c #5c5c60",
"iW c #5c80a4",
"#G c #5c82a5",
"#N c #5c88ad",
"#. c #5c88ae",
"ig c #5c8bb3",
"ox c #5d5d61",
".U c #5d7386",
"#W c #5d738c",
".c c #5d7c99",
"as c #5d7c9c",
".3 c #5d82a4",
"#n c #5d83a4",
"fy c #5d85a8",
"jj c #5d8bb3",
"c5 c #5d8bb4",
"nX c #5e5742",
"l1 c #5e88ad",
"cL c #5e8bb3",
"fC c #5e8cb4",
"gc c #5e8db4",
"ir c #5f5f61",
".V c #5f6a75",
"ed c #5f83a5",
"aS c #5f86a8",
"lk c #5f8cb5",
"iD c #5f8db4",
"m6 c #60563a",
"i. c #6082a4",
"iY c #6084a5",
"l0 c #6085a5",
"#k c #6185a6",
"cK c #618db5",
"#O c #618eb5",
"oh c #626266",
".e c #626d78",
".M c #627181",
".q c #627384",
"#0 c #6288a9",
"h5 c #636366",
"iV c #6385a5",
"gb c #6388aa",
"fe c #638fb5",
"dp c #638fb6",
"dn c #638fb7",
"nP c #646467",
"aP c #64819f",
"gD c #648ea8",
"bt c #656569",
"fa c #658cae",
"c6 c #658fb6",
"i0 c #6590b6",
"bW c #6594bd",
"nL c #665b3c",
"mJ c #666152",
".4 c #6688a8",
"lG c #6692b9",
"iO c #676768",
"az c #678caf",
"iZ c #678cb0",
"ie c #6790b7",
"if c #6791b7",
"aU c #6792b7",
"dS c #6888a7",
"eb c #688aa9",
"ef c #6891b8",
"li c #698aa9",
"a8 c #698cab",
"ah c #6992b7",
"i1 c #6992b8",
"a1 c #6a6452",
"nC c #6a6a6c",
"dR c #6a87a4",
"#H c #6a8baa",
"dU c #6a8bab",
"#S c #6a8fb1",
"eC c #6a92b6",
"dr c #6a93b7",
"g. c #6b87a3",
"#w c #6b90b1",
"bk c #6c87a1",
"fq c #6c87a3",
"et c #6c89a6",
"ag c #6c91b3",
"#6 c #6c92b2",
"gB c #6c94b8",
"iT c #6d88a5",
"je c #6d89a5",
"jI c #6d89a6",
"kj c #6d8aa6",
"gR c #6d94b8",
"eX c #6d95b8",
".d c #6e879f",
"iv c #6e88a5",
"gl c #6e89a5",
"jv c #6e89a6",
"hg c #6f94b7",
"nw c #70623b",
"bC c #709dc1",
"c7 c #7197b9",
"hc c #728da9",
"gA c #728daa",
"ey c #7290ae",
"hI c #7296b6",
"dj c #729ec2",
"ou c #737376",
"ez c #7391af",
"dZ c #7397bb",
"ak c #7398b6",
"aA c #7398b9",
"ji c #7398bb",
"l3 c #739bbe",
"no c #747474",
"bO c #747477",
"mn c #747478",
"hH c #7499ba",
"lU c #757578",
"jg c #7591ad",
"#3 c #7599b9",
"eg c #7599ba",
"mZ c #767676",
"dq c #7699bb",
"eV c #7795b1",
"gT c #7797aa",
"jh c #7896b5",
"dt c #789cbc",
"d2 c #789dbd",
"e5 c #798fa8",
"## c #7995af",
"ff c #799dbd",
"dA c #7a95ae",
"lD c #7a97b1",
"aF c #7a9cb3",
"eG c #7a9fbf",
"c9 c #7b9ebd",
"aD c #7b9fbe",
"ei c #7b9fbf",
"fD c #7ba0bf",
"aT c #7c9db9",
"eD c #7c9dbe",
"dH c #7c9ebd",
"bn c #7c9fbf",
"co c #7ca0c0",
"dT c #7d96b2",
"fd c #7d9ebc",
"bE c #7d9fc0",
"b1 c #7da0c0",
"h8 c #7e94ac",
"bx c #7e98b1",
"eE c #7e9ebe",
"ll c #7ea2c2",
"cb c #7f7f80",
"el c #7f7f83",
"eQ c #7f97af",
"dm c #7fa1c0",
"e. c #8095ac",
"di c #809ab3",
"gq c #809b9f",
"bU c #819cb4",
"bj c #82858c",
"ec c #829ab4",
"k0 c #8398af",
"#1 c #839eb9",
"cG c #83aaca",
"gd c #84a3bf",
"mB c #858589",
"g6 c #85a2bd",
"bZ c #85aaca",
"nW c #877748",
"n0 c #88888a",
"or c #88888d",
"hT c #889bb1",
"nQ c #89898a",
"hu c #89a3b8",
"cq c #89a5bb",
"dG c #89a5c2",
"dF c #89a6c0",
"ai c #89a7c2",
"n7 c #8a8884",
"e1 c #8a898c",
"d0 c #8aa5c1",
"cM c #8aa6c0",
"mc c #8b8b90",
"j9 c #8ba2a8",
"lZ c #8ba2b9",
"jJ c #8ba3a8",
"kL c #8ba3a9",
"iE c #8ca4a8",
"lH c #8dacc9",
"is c #919192",
"eK c #919195",
"gE c #91a4a1",
"jc c #929293",
"#2 c #92a7bd",
"hW c #92aabf",
"nK c #938146",
"bu c #949496",
"ol c #949498",
"eW c #94a9bf",
"lh c #95abbe",
"mS c #968964",
"ih c #96aab3",
"my c #999797",
"g7 c #99acbb",
"d1 c #9aafc6",
"aI c #9b8c5f",
"nD c #9b9a9b",
"cy c #9b9a9e",
"og c #9b9ba0",
"es c #9ba9ba",
"mg c #9bafc4",
"cm c #9cbad4",
"dQ c #9dabbb",
"k4 c #9daeb4",
"cj c #9dbbd5",
"d5 c #9e9ea3",
"hJ c #9eacaa",
"nh c #9f9473",
"l4 c #9fb6cf",
"bf c #a09268",
"o# c #a1a0a3",
"bP c #a1a1a3",
"hh c #a1b0b6",
"eP c #a3acbb",
"gU c #a3aeaa",
"lC c #a3b3c4",
"mf c #a4b2c3",
"mO c #a5a5a8",
"na c #a7a7aa",
"lm c #a7bed3",
"n1 c #a9a9a8",
"cQ c #acabae",
"n6 c #acacae",
"da c #adacaf",
"mm c #adadb2",
"c0 c #adc6dc",
"lg c #afb7c3",
"dv c #b0afb3",
"iP c #b0b0b2",
"gr c #b1b9ad",
"lY c #b1bcc9",
"mh c #b1c1d4",
"em c #b2b2b5",
"js c #b3b3b6",
"dz c #b3b9c4",
"bX c #b3cade",
"nv c #b49d59",
"m5 c #b6a36a",
"nV c #b6a573",
"cc c #b6b6b6",
"nR c #b6b6b7",
"e2 c #b7b7b9",
"ow c #b7b7bd",
"lI c #b7c7d9",
"cE c #b9cfe1",
"gF c #baba9d",
"np c #bbbabb",
"mI c #bcaa78",
"hv c #bcba99",
"ov c #bcbcbf",
"m0 c #bdbdc0",
"gZ c #bdbdc2",
"g8 c #bebea8",
"fk c #bebec1",
"d9 c #bec4cc",
"hX c #bfbc98",
"om c #bfbec0",
"dL c #bfbec2",
"eL c #bfbfbf",
"gJ c #c0c0c2",
"fK c #c0c0c3",
"bv c #c1c1c0",
"gk c #c1c5cc",
"a0 c #c3ae6f",
"it c #c3c3c5",
"jd c #c3c3c6",
"mC c #c4c3c8",
"lB c #c4cad2",
"bq c #c5b582",
"oa c #c5c4c3",
"mp c #c6c6c6",
"me c #c6cbd1",
"of c #c7c7c9",
"l5 c #c7d1de",
"d6 c #c8c7cb",
"lT c #c8c8c9",
"ln c #cacdc7",
"dy c #cbccd1",
"f9 c #cbd0d5",
"ha c #cccccf",
"bw c #cccfd3",
"hK c #cdbf7a",
"hl c #cdcccf",
"jV c #cdccd0",
"nE c #cdcdcd",
"g0 c #cdcdd2",
"ng c #ceb568",
"hi c #cec9b0",
"hC c #cecdcf",
"h6 c #cecdd1",
"nb c #ceced0",
"j5 c #ceced2",
"kf c #ceced3",
"nJ c #cfb35b",
"kE c #cfcdd1",
"le c #cfced1",
"lx c #cfced2",
"bQ c #cfcfcf",
"mx c #d0be88",
"lz c #d0ced2",
"iu c #d0cfd2",
"jE c #d0cfd3",
"md c #d0d0d3",
"hS c #d0d2d7",
"h7 c #d0d3d7",
"gz c #d0d4da",
"ms c #d1d0d4",
"mE c #d1d0d5",
"jt c #d1d1d3",
"iS c #d1d1d4",
"nc c #d1d1d5",
"kK c #d1d5d8",
"be c #d2b65c",
"n2 c #d2d1d0",
"jW c #d2d1d6",
"dP c #d2d6d9",
"j7 c #d2d6da",
"ki c #d2d7da",
"mR c #d3ba71",
"m1 c #d3d2d4",
"n5 c #d3d3d5",
"gN c #d3d3d7",
"jH c #d3d6da",
"ju c #d3d7da",
"bY c #d3e1ec",
"jD c #d4d3d5",
"eq c #d4d3d6",
"cZ c #d4e1ed",
"hY c #d5c270",
"gV c #d5c78e",
"d7 c #d5d4d6",
"iQ c #d5d5d6",
"lf c #d5d5d8",
"hp c #d5d5d9",
"er c #d5d8da",
"ho c #d6d5d7",
"de c #d6d6d8",
"hG c #d6dbdf",
"j6 c #d7d7d9",
"lX c #d7dadb",
"dh c #d7dadd",
"cd c #d8d8d9",
"fp c #d8dce0",
"aH c #d9bc62",
"cz c #d9d9d9",
"g1 c #d9d9db",
"cS c #dad9db",
"kg c #dad9dd",
"jC c #dadad9",
"e4 c #dadee1",
"g3 c #dadee2",
"gP c #dadfe2",
"jT c #dbdad9",
"gx c #dbdadb",
"lw c #dbdbd9",
"gM c #dbdbdb",
"f8 c #dbdbdc",
"gy c #dbdbdd",
"lJ c #dbe1e8",
"on c #dcdbd8",
"lV c #dcdbda",
"en c #dcdcdb",
"kI c #dcdcde",
"m4 c #ddc67d",
"j4 c #dddcdc",
"fn c #dddcde",
"ko c #ded0a6",
"fL c #dedddd",
"cT c #dedede",
"hb c #dededf",
"ob c #dfdedc",
"dd c #dfdede",
"kq c #dfdee1",
"f7 c #dfdfde",
"nu c #e0c162",
"fi c #e0cd94",
"kH c #e0dfdf",
"mb c #e0e0e2",
"bT c #e0e2e4",
"cg c #e0e2e5",
"mi c #e0e5ea",
"bp c #e1c87e",
"k5 c #e1cc87",
"nU c #e1dbcd",
"df c #e1e1e1",
"gs c #e2cd86",
"jZ c #e2cd8c",
"ka c #e2ce94",
"j0 c #e2ce95",
"fM c #e2e1e1",
"kZ c #e2e1e2",
"eM c #e2e2e0",
"fl c #e2e2e1",
"fO c #e2e2e2",
"oq c #e2e2e3",
"eO c #e2e3e5",
"hw c #e3c865",
"ge c #e3cb75",
"jP c #e3cd8f",
"k# c #e3ce90",
"jz c #e3cf93",
"ip c #e3cf94",
"jA c #e3dac3",
"ly c #e3e2e1",
"mD c #e3e2e2",
"fm c #e3e3e2",
"cA c #e3e3e3",
"gO c #e3e3e4",
"lA c #e3e4e2",
"kM c #e4cc7b",
"hQ c #e4e3e1",
"hR c #e4e3e2",
"kG c #e4e3e3",
"dx c #e4e4e2",
"fN c #e4e4e3",
"kJ c #e4e4e4",
"ii c #e5cd77",
"kn c #e5ce88",
"iL c #e5dfd1",
"gj c #e5e4e2",
"kh c #e5e4e4",
"bR c #e5e5e4",
"aG c #e6c550",
"hL c #e6c960",
"mq c #e6e5e1",
"cR c #e6e5e3",
"hF c #e6e5e4",
"fo c #e6e5e5",
"d8 c #e6e7e4",
"dO c #e6e7e5",
"gG c #e7cc70",
"gw c #e7e6e4",
"jF c #e7e6e6",
"hn c #e7e7e4",
"kp c #e7e7e5",
"cU c #e7e7e6",
"lS c #e7e7e9",
"cN c #e8cd6d",
"fJ c #e8cf82",
"ky c #e8d28f",
"jq c #e8e3d8",
"hE c #e8e7e4",
"nd c #e8e7e5",
"ep c #e8e7e7",
"gL c #e8e8e5",
"cV c #e8e8e8",
"oe c #e8e8e9",
"cr c #e9ca5f",
"hZ c #e9cb64",
"mH c #e9cb6a",
"jK c #e9cc6c",
"ks c #e9cd6c",
"hy c #e9d28a",
"ke c #e9e8e5",
"gv c #e9e8e6",
"nF c #e9e8e7",
"cB c #e9e8e8",
"mt c #e9e8e9",
"kr c #e9e9e7",
"mF c #e9e9e9",
"m2 c #e9e9ea",
"iF c #eacd6c",
"jQ c #eace7d",
"kO c #eacf7a",
"lo c #ead17f",
"jR c #ead38a",
"jp c #eadcb4",
"nS c #eae8e7",
"hm c #eae9e6",
"gK c #eae9e7",
"hq c #eaeae9",
"oo c #eaeaec",
"fG c #ebcf79",
"ku c #ebcf7a",
"kv c #ebd07f",
"jm c #ebd286",
"k. c #ebd287",
"i8 c #ebe2c8",
"j3 c #ebeae7",
"dN c #ebebe7",
"lW c #ebebe8",
"dg c #ebebea",
"jO c #ecce71",
"fh c #ecce74",
"gH c #ecd385",
"mk c #ecd386",
"mr c #eceae7",
"nq c #eceae8",
"dc c #ecebe7",
"gi c #ecebe8",
"nI c #edc95a",
"gg c #edce72",
"bL c #edd076",
"hO c #edd384",
"lp c #edd484",
"kY c #edece8",
"hD c #edece9",
"ne c #edeced",
"eo c #ededeb",
"ce c #ededec",
"n4 c #ededef",
"l6 c #edf0f2",
"aZ c #eec954",
"d. c #eecc53",
"fZ c #eecc54",
"f2 c #eecf70",
"gh c #eecf72",
"i3 c #eed385",
"j2 c #eed483",
"ik c #eed484",
"iG c #eed485",
"ml c #eeebe3",
"i9 c #eeede8",
"dM c #eeede9",
"jU c #eeedea",
"cW c #eeeeed",
"nr c #eeeeee",
"nG c #eeeeef",
"aW c #efc431",
"b2 c #efc432",
"mw c #efc84d",
"nf c #efcb5d",
"eH c #efcc53",
"g9 c #efce60",
"kN c #efcf6a",
"km c #efcf6b",
"f1 c #efcf6c",
"f4 c #efd06e",
"fH c #efd171",
"iI c #efd172",
"gX c #efd47f",
"hA c #efd480",
"lc c #efd483",
"kX c #efd484",
"iJ c #efd992",
"mG c #efe7d3",
"mP c #efe8d3",
"lL c #efe9d3",
"kF c #efedea",
"dw c #efeee9",
"db c #efeeea",
"iR c #efefef",
"cF c #eff4f8",
"bd c #f0c12b",
"bH c #f0cd5d",
"io c #f0cf69",
"kP c #f0d068",
"k7 c #f0d069",
"e0 c #f0d06a",
"h2 c #f0d06b",
"nH c #f0d379",
"h1 c #f0d380",
"im c #f0d480",
"i7 c #f0dea8",
"iK c #f0e4bf",
"ns c #f0e7d3",
"lv c #f0f0f3",
"ck c #f0f5f9",
"fF c #f1cf65",
"k6 c #f1cf67",
"bI c #f1cf68",
"hj c #f1d064",
"jY c #f1d066",
"gf c #f1d067",
"c. c #f1d16b",
"cw c #f1d16c",
"jo c #f1d98d",
"e3 c #f1f1ef",
"ld c #f1f1f2",
"j# c #f1f1f3",
"bo c #f2c848",
"hx c #f2cd52",
"b4 c #f2ce5e",
"f3 c #f2cf63",
"kb c #f2d16a",
"i6 c #f2d478",
"jG c #f2f2f0",
"hM c #f3cf56",
"i5 c #f3d062",
"jn c #f3d26c",
"jB c #f3f2f0",
"bS c #f3f3f0",
"cC c #f3f3f1",
"ma c #f3f3f4",
"mQ c #f4cf5a",
"jy c #f4d05d",
"fI c #f4d05f",
"kQ c #f4d060",
"kw c #f4d061",
"m3 c #f4d370",
"lM c #f4d470",
"g2 c #f4f4f1",
"b3 c #f5cb49",
"b5 c #f5cf57",
"bJ c #f5cf58",
"iH c #f5d05b",
"kt c #f5d05d",
"kR c #f5d15e",
"f5 c #f5d161",
"eN c #f5f5f2",
"op c #f5f5f6",
"cs c #f6cf58",
"f0 c #f6d057",
"ct c #f6d260",
"cX c #f6f6f3",
"od c #f6f6f7",
"nt c #f7d053",
"b6 c #f7d157",
"kx c #f7d158",
"kz c #f7d25d",
"cf c #f7f7f4",
"lK c #f7f7f8",
"gW c #f8d150",
"jx c #f8d153",
"k8 c #f8d154",
"j1 c #f8d157",
"b7 c #f8d259",
"hP c #f8d25b",
"hz c #f8d25c",
"jX c #f8d25d",
"jk c #f8d35b",
"il c #f8d35c",
"lR c #f8f8f9",
"h0 c #f9d14e",
"fg c #f9d151",
"bK c #f9d153",
"gt c #f9d256",
"cu c #f9d257",
"bc c #fac723",
"mv c #facf46",
"i4 c #fad14f",
"jN c #fad151",
"jr c #fafaf9",
"n3 c #fafafb",
"jw c #fbd14c",
"kc c #fbd24d",
"bG c #fcc925",
"hk c #fcd147",
"jM c #fcd14a",
"in c #fcd24a",
"gI c #fcd24b",
"h. c #fcd24c",
"b8 c #fcd24d",
"jS c #fcf9f0",
"mj c #fcfcfc",
"m# c #fcfcfd",
"mu c #fdd145",
"fE c #fdd146",
"jl c #fdd147",
"jL c #fdd246",
"hN c #fdd247",
"kd c #fdd248",
"cO c #fdd249",
"hB c #fdd24c",
"oc c #fdfdfd",
"nT c #fdfdfe",
"aY c #fec91f",
"bb c #fec921",
"gu c #fed243",
"cv c #fed244",
"ij c #fed246",
"j. c #fefefe",
"aX c #ffc91d",
"b9 c #ffd242",
"f6 c #ffd243",
"m. c #ffd347",
"kD c #ffd348",
"l7 c #ffd449",
"kS c #ffd44b",
"kA c #ffd54f",
"lu c #ffd654",
"kW c #ffd757",
"lb c #ffd85d",
"l9 c #ffda65",
"kC c #ffdc6c",
"k9 c #ffdd71",
"l8 c #ffde74",
"lQ c #ffdf77",
"lN c #ffdf78",
"kB c #ffe07e",
"lq c #ffe48e",
"kT c #ffe9a2",
"lt c #ffefbf",
"kV c #fff2c6",
"lO c #fff2c8",
"lP c #fff3cc",
"kU c #fff5d7",
"l. c #fff7de",
"la c #fffae9",
"lr c #fffaec",
"l# c #fffefd",
"ls c #fffffe",
"cl c #ffffff",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.#.a.b.c.d.e.f.gQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.g.h.i.j.k.l.m.n.o.p.q.r.sQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.t.u.v.w.x.y.z.A.A.A.B.C.D.E.F.G.H.IQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.J.K.L.M.N.O.P.Q.A.A.A.A.A.A.A.A.R.S.T.U.V.WQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.g.X.Y.Z.0.1.2.3.4.5.6.A.A.A.A.A.A.A.A.7.8.9#.###aQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#b#c#d#e#f#g#h.A#i#j#k#l#m#n#o.Q.A.A#p#q#r#s#t#u#v#w#xQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.##y#z#A#B#C#D#E.A.A.A.A.A.A#F#G#H#I#J#K#L#M#N#O#P#Q#R#R#S#TQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#U#V#W#X#Y.A.A.A.A.A.A.A.A.A.A.A#Z#0#1#2#3#4#5#R#R#R#R#R#6#7.gQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#8#9a.a#aaabac.z.A.A.A.A.A.zadaeafagahaiaj#R#R#R#R#R#R#RakalamanaoQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtapaq.A#parasatau.mav.QawaxayazaAaBaC#RaDaE#R#R#R#R#R#R#RaFaGaHaIaJaKaL.gQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaMaN.A.A.A.AabaOaPaQaRaSaTaUaV#R#R#R#RaDaE#R#R#R#R#R#R#RaFaWaXaYaZa0a1a2a3.g",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaMa4.A.A.A.A.Ba5a6a7a8a9b.b#ba#R#R#R#RaDaE#R#R#R#R#R#R#RaFaWaXaXbbbcbdbebfbg",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbhbibjbk.A.A.A.A.A.A.A#hblbm#R#R#R#R#R#R#RbnaE#R#R#R#R#R#R#RaFaWaXaXaXaXaYbobpbq",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbrbsbtbubvbwbx.A.A.A.A.A.A.A#pbybzbAbBbCbD#R#R#RbEbF#R#R#R#R#R#R#RaFaWaXaXbGbHbIbJbKbL",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbMbNbObPbQbRbSbTbU.A.A.A.A.A.A.A#pbybVbWbXbYbZb0#R#Rb1bF#R#R#R#R#R#R#RaFb2b3b4b5b6b7b8b9c.",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtc#cacbcccdcecfcfcfcgbU.A.A.A.A.A.A.A#pchcicjckclcmcn#R#RcobF#R#R#R#R#Rbacpcqcrcsctcucvb9b9b9cw",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtcxcyczcAcBcCcfcfcfcgbU.A.A.A.A.A.A.A#pchcDcEclcFcGcH#R#RcobF#R#R#RcIcJcKcLcMcNcOb9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtcPcQcRcScTcUcVcWcXbTbU.A.A.A.A.A.A.A#pbycYcjcZc0c1#R#R#Rb1bFc2c3c4c5c6c7c8c9d.b9b9b9b9b9b9b9c.",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtd#dadbdbdcdddedfdgdhdi.B.A.A.A.A.A.A#pbybzc1djdkdl#R#R#Rdmdndodpdqdrds#R#Rdtd.b9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdudvdbdbdbdbdwdxdydzdA#odBdC.A.A.A.A#pchbV#R#R#R#Rb#dDdEdFdGdHbmbA#R#R#R#Rdtd.b9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQt.gdIdJdKdLdbdbdMdNdOdPdQdRdSdTdUdVdWav.B.A#pchbz#Rbab#dXdYdZd0d1do#R#R#R#R#R#R#Rd2d.b9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtd3d4d5d6d7dbdbd8d9e.e#.6.B.B.QeaebeceddB.AdCeebV#RdDefd0egehbAeiaE#R#R#R#R#R#R#Rd2d.b9b9b9b9b9b9b9c.",
"QtQtQtQtQtQtQtQtQtQtQtejekelemeneoepeqereseteuacav.A.A.A.A.QevewexeyezeAeBeCeDeEeF#vdXbA#ReGaE#R#R#R#R#R#R#Rd2eHb9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtQteIeJeKeLeMeoeNcfeOePeQeRaceS.A.A.A.A.A.A.A.A.A.6eTeUeVeWeXeYeZb.#R#R#R#ReGaE#R#R#R#R#R#R#Rd2d.b9b9b9b9b9b9b9e0",
"QtQtQtQtQtQtQtQtbMe1e2cTe3cfcfcfcfe4e5e6e7e8e9eS.A.A.A.A.A.A.Af.f#fafbfcfdfebA#R#R#R#R#R#ReiaE#R#R#R#R#R#R#RffeHb9b9b9b9b9fgfhfi",
"QtQtQtQtQtQtQtQtfjfkflfmfnfocXcfcffpfqavfrfsftfufvfw.A.A#EfxfyfzfAfBeh#QeXfC#R#R#R#R#R#R#RfDaE#R#R#R#R#R#R#Rd2d.b9b9fEfFfGfHfIfJ",
"QtQtQtQtQtQtQtQtfjfKdMcRfLfMfNfOeofpfq.A.A.B#hfPfQfRfSfTfUfVfWfXfY#R#R#ReXfC#R#R#R#R#R#R#RfDaE#R#R#R#R#R#R#Rd2fZf0f1f2f3f4f5f6cw",
"QtQtQtQtQtQtQtQtfjfKdbdbdbdcf7f8cAf9g..A.A.A.A.A.zg#gagb#4dEba#R#R#R#R#ReXgc#R#R#R#R#R#R#RfDaE#R#R#R#R#R#RdDgdgegfggghcOb9b9b9c.",
"QtQtQtQtQtQtQtQtfjfKdbdbdbdbgigjddgkgl.A.A.A.A.A.A.Agmgn#R#R#R#R#R#R#R#ReXfC#R#R#R#R#R#R#RfDaE#R#R#Rb#gogpgqgrgsgtgub9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtfjfKdbdMgvgwcRgxgygzgA.A.A.A.A.A.A.Agmgn#R#R#R#R#R#R#R#RgBfC#R#R#R#R#R#R#ReiaEba#vgCgDgEgFgGgHgIb9b9b9b9b9b9b9c.",
"QtQtQtQtQtQtQtQtfjgJgKgLgMgNgOcXcfgPgA.A.A.A.A.A.A.AgQgn#R#R#R#R#R#R#R#RgRfC#R#R#R#R#R#R#Rbn#4gSgTgUgVgWb9b9gXgIb9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQtgYgZg0g1g2cfcfcfcfg3gA.A.A.A.A.A.A.Ag4g5#R#R#R#R#R#R#R#RgRfC#R#R#R#Rba#Qb.g6g7g8g9cvb9b9b9b9gXh.b9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQth#d6hahbcfcfcfcfcfg3hc.A.A.A.A.A.A.Ahdhe#R#R#R#R#R#R#R#RgRfC#R#R#R#Rhfhghhhihjhkgub9b9b9b9b9gXh.b9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQth#hlhmhnhohphqcfcfgPhc.A.A.A.A.A.A.Ahrhs#R#R#R#R#R#R#R#RgRfC#Rhtc5huhvhwhxhyhzb9b9b9b9b9b9b9hAhBb9b9b9b9b9b9b9cw",
"QtQtQtQtQtQtQtQth#hCdbhDhEhFfOdehbhGhc.A.A.A.A.A.A.Ahrhs#R#R#R#R#R#R#R#ReXhHhIhJhKhLhMhNb9hOhPb9b9b9b9b9b9b9hAgIb9b9b9b9b9b9b9c.",
"QtQtQtQtQtQtQtQth#hCdbdbdbdMgKhQhRhShThUaw.A.A.A.A.Ahrhs#R#R#R#R#R#RhVfChWhXhYhZh0f6b9b9b9hOhPb9b9b9b9b9b9b9h1h.b9b9b9b9b9b9b9h2",
"QtQtQtQtQtQth3h4h5h6dbdbdbdbdbdbhDh7h8h9i.i#iaib.A.Aichs#R#R#RaCidieifigihiiijb9b9b9b9b9b9ikilb9b9b9b9b9b9b9imhBb9b9b9b9b9inioip",
"QtQtQt.gdJiqirisitiudbdbdbdbdbdbdbdPiviwixiyiziAex.ChrhsbAiBiCdpiDcK#4dDiEiFb9b9b9b9b9b9b9iGhzb9b9b9b9b9b9b9imhBb9b9cviHiIiJiKiL",
"QtbriMiNiOiPiQiRe3iSdbdbdbdbdbdbdbdPiT.A.AaviUiViWiXiYiZiCc5i0i1i2#R#R#RiEiFb9b9b9b9b9b9b9i3hzb9b9b9b9b9b9b9imh.i4i5i6i7i8i9j.j#",
"jajbjcjdcUcfcfcfe3iSdbdbdbdbdbdbdbdPje.A.A.A.A.A.Cjfjgjhjijjb##R#R#R#R#RiEiFb9b9b9b9b9b9b9i3jkb9b9b9b9b9b9jljmjnjojpjqjrclclclj#",
"jsjteog2cfcfcfcfe3iSdbdbdbdbdbdbdbjujv.A.A.A.A.A.A.Agmgn#R#R#R#R#R#R#R#RiEiFb9b9b9b9b9b9b9i3ilb9b9b9gujwjxjyjzjAjBj.clclclclclj#",
"jCjDjEjFjGcCcXcfe3iSdbdbdbdbdbdbdbjHjI.A.A.A.A.A.A.Agmgn#R#R#R#R#R#R#R#RjJjKb9b9b9b9b9b9b9i3ilb9jLjMjNjOjPjQjRjSclclclclclclclj#",
"jTdbjUgjjVjWe3cXe3iSdbdbdbdbdbdbdbjHjI.A.A.A.A.A.A.Agmgn#R#R#R#R#R#R#R#RjJjKb9b9b9b9b9b9b9i3jXcvjYjZj0j1f6b9j2jSclclclclclclclj#",
"jTdbdbdMgij3j4j5j6iQdbdbdbdbdbdbdbj7jv.A.A.A.A.A.A.AgQj8#R#R#R#R#R#R#R#Rj9jKb9b9b9b9b9b9b9k.k#kakbkckdcvb9b9j2jSclclclclclclclj#",
"jTdbdbdbdbdbj3kecRkfkgkhdMdbdbdbdbkikj.A.A.A.A.A.A.Akkkl#R#R#R#R#R#R#R#Rj9jKb9b9b9b9inkmknkokbfgcOf6b9b9b9b9j2jSclclclclclclclj#",
"jTdbdbdbdbdbdbdbhmh6kpfNg1kqkrdbdbkikj.A.A.A.A.A.A.Ahrhs#R#R#R#R#R#R#R#Rj9ksb9cvktkukvkwkxkykzb9b9b9kAkBkCkDj2jSclclclclclclclj#",
"jTdbdbdbdbdbdbdbhmkEkFj3kGkhkHkIkJkKjI.A.A.A.A.A.A.Ahrhs#R#R#R#R#R#R#R#RkLkMkNkOkPkQkRgIb9hOhPb9b9kSkTkUkVkWkXjSclclclclclclclj#",
"jTdbdbdbdbdbdbdbhmkEdbdbdbkYcRkZkGbwk0k1eS.A.A.A.A.Ahrhs#R#R#R#R#R#Rk2k3k4k5k6k7k8gub9b9b9hOhPb9b9k9l.l#lalblcjSclclclclclclclld",
"jTdbdbdbdbdbdbdbhmledbdbdbdbdbdbj3lflglhlihUiw.A.A.Ahrhs#R#R#RljeZlklllmlnloinb9b9b9b9b9b9lpilb9b9lqlrlsltlulcjSclclclclclj.lvj6",
"lwdbdbdbdbdbdbdbhmlxdbdbdbdbdbdbdblylzlAlBlClD#Kia.6hrhs#RlElFlGlHlIlJlKlLlMb9b9b9b9b9b9b9iGjkb9b9lNlOlPlQkDkXjSclclcllRlSlTeKlU",
"lVdbdbdbdbdbdbdbhmlxdbdbdbdbdbdbdblyiudbdblWlXlYlZl0#ll1l2l3l4l5l6j.clcllLlMb9b9b9b9b9b9b9i3hzb9b9l7l8l9m.b9lcjSm#mambjsmcd4.#Qt",
"jTdbdbdbdbdbdbdbhmlxdbdbdbdbdbdbdblymddbdbdbdbdbcRmemfmgmhmimjclclclclcllLlMb9b9b9b9b9b9b9i3hzb9b9b9b9b9b9cvmkmliQmmmnmo.gQtQtQt",
"mpmqmrdMdbdbdbdbhmlxdbdbdbdbdbdbdblymsdbdbdbdbdbdbdbgjmtclclclclclclclcllLlMb9b9b9b9b9b9b9i3hzb9b9b9b9mumvmwmxmymz.IQtQtQtQtQtQt",
"mAmBmCkhdbdbdbdbhmlxdbdbdbdbdbdbdbmDmEdbdbdbdbdbdbdbcRmFclclclclclclclclmGlMb9b9b9b9b9b9b9i3ilb9b9b9cvmHmImJmKh3.gQtQtQtQtQtQtQt",
"brmLmMmNmOd7gidbhmlxdbdbdbdbdbdbdbmDmEdbdbdbdbdbdbdbcRmFclclclclclclclclmPlMb9b9b9b9b9b9b9i3ilb9mQmRmSmTmUmV.gQtQtQtQtQtQtQtQtQt",
"QtQtQtmWmXmYmZm0f8m1dbdbdbdbdbdbdbfmmsdbdbdbdbdbdbdbcRm2clclclclclclclclmPm3b9b9b9b9b9b9b9gHm4m5m6m7m8m9QtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtbrh#n.n#nanbkpdbdbdbdbdbmDncdbdbdbdbdbdbdbndneclclclclclclclclmPlMb9b9b9b9ijnfngnhninjnkbrQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtnlnmnnnonpgxnqdbdbfmncdbdbdbdbdbdbdbgKnrclclclclclclclclnsm3b9b9ntnunvnwnxnynzQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtbrnAnBnCnDnEhRlyncdbdbdbdbdbdbdbnFnGclclclclclclclclnsnHnInJnKnLnMnNQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQt.JnOnPnQnRh6gKdbdbdbdbdbdbnSnGclclclclclclnTlKnUnVnWnXnYh3QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtc#nZn0n1n2hQhDdbdbdbkrnGclclclcln3n4n5n6n7n8n9QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQth3o.bOo#oaobhmdMgKnGclocodoeofogohoiQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtojokolomonlyooopoqgZorosbrQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtotouovgNowoxmLQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt"};
"""

if __name__ == "__main__":
    dlg = Packages()
    if not FreeCADGui.Control.activeDialog():
        FreeCADGui.Control.showDialog(dlg)
    else:
        FreeCAD.Console.PrintError("Already there is an active task dialog.\n")

Lien

La discussion du forum Macro Packages