Example #1
0
 def is_installed(self):
     if self.on_git and not self.src_filename:
         return False
     return (os.path.exists(
         os.path.join(FreeCAD.getUserMacroDir(True), self.filename))
             or os.path.exists(
                 os.path.join(FreeCAD.getUserMacroDir(True),
                              "Macro_" + self.filename)))
Example #2
0
 def __init__(self):
     commands_dialog = gui.PySideUic.loadUi(
         app.getUserMacroDir(True)
         + '/MultiCopy/resources/MultiCopy_Commands_Dialog.ui'
     )
     commands_dialog.findChild(QPushButton, 'okay_button').clicked.connect(
         lambda: commands_dialog.done(1)
     )
     commands_dialog.setWindowIcon(
         QIcon(app.getUserMacroDir(True) + 'MultiCopy.svg')
     )
     commands_dialog.exec_()
    def check_macro(self, macro_wrapper: Addon) -> None:
        """Check to see if the online copy of the macro's code differs from the local copy."""

        # Make sure this macro has its code downloaded:
        try:
            if not macro_wrapper.macro.parsed and macro_wrapper.macro.on_git:
                macro_wrapper.macro.fill_details_from_file(
                    macro_wrapper.macro.src_filename
                )
            elif not macro_wrapper.macro.parsed and macro_wrapper.macro.on_wiki:
                mac = macro_wrapper.macro.name.replace(" ", "_")
                mac = mac.replace("&", "%26")
                mac = mac.replace("+", "%2B")
                url = "https://wiki.freecad.org/Macro_" + mac
                macro_wrapper.macro.fill_details_from_wiki(url)
        except Exception:
            FreeCAD.Console.PrintWarning(
                translate(
                    "AddonsInstaller",
                    "Failed to fetch code for macro '{name}'",
                ).format(name=macro_wrapper.macro.name)
                + "\n"
            )
            macro_wrapper.set_status(Addon.Status.CANNOT_CHECK)
            return

        hasher1 = hashlib.sha1()
        hasher2 = hashlib.sha1()
        hasher1.update(macro_wrapper.macro.code.encode("utf-8"))
        new_sha1 = hasher1.hexdigest()
        test_file_one = os.path.join(
            FreeCAD.getUserMacroDir(True), macro_wrapper.macro.filename
        )
        test_file_two = os.path.join(
            FreeCAD.getUserMacroDir(True), "Macro_" + macro_wrapper.macro.filename
        )
        if os.path.exists(test_file_one):
            with open(test_file_one, "rb") as f:
                contents = f.read()
                hasher2.update(contents)
                old_sha1 = hasher2.hexdigest()
        elif os.path.exists(test_file_two):
            with open(test_file_two, "rb") as f:
                contents = f.read()
                hasher2.update(contents)
                old_sha1 = hasher2.hexdigest()
        else:
            return
        if new_sha1 == old_sha1:
            macro_wrapper.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
        else:
            macro_wrapper.set_status(Addon.Status.UPDATE_AVAILABLE)
Example #4
0
    def remove(self) -> bool:
        """Remove a macro and all its related files

        Returns True if the macro was removed correctly.
        """

        if not self.is_installed():
            # Macro not installed, nothing to do.
            return True
        macro_dir = FreeCAD.getUserMacroDir(True)
        macro_path = os.path.join(macro_dir, self.filename)
        macro_path_with_macro_prefix = os.path.join(macro_dir, "Macro_" + self.filename)
        if os.path.exists(macro_path):
            os.remove(macro_path)
        elif os.path.exists(macro_path_with_macro_prefix):
            os.remove(macro_path_with_macro_prefix)
        # Remove related files, which are supposed to be given relative to
        # self.src_filename.
        for other_file in self.other_files:
            dst_file = os.path.join(macro_dir, other_file)
            try:
                os.remove(dst_file)
                remove_directory_if_empty(os.path.dirname(dst_file))
            except Exception:
                FreeCAD.Console.PrintWarning(
                    translate(
                        "AddonsInstaller",
                        "Failed to remove macro file '{}': it might not exist, or its permissions changed",
                    ).format(dst_file)
                    + "\n"
                )
        return True
Example #5
0
def remove_macro(macro):
    """Remove a macro and all its related files

    Returns True if the macro was removed correctly.

    Parameters
    ----------
    - macro: a addonmanager_macro.Macro instance
    """
    if not macro.is_installed():
        # Macro not installed, nothing to do.
        return True
    macro_dir = FreeCAD.getUserMacroDir(True)
    macro_path = os.path.join(macro_dir, macro.filename)
    macro_path_with_macro_prefix = os.path.join(macro_dir, 'Macro_' + macro.filename)
    if os.path.exists(macro_path):
        os.remove(macro_path)
    elif os.path.exists(macro_path_with_macro_prefix):
        os.remove(macro_path_with_macro_prefix)
    # Remove related files, which are supposed to be given relative to
    # macro.src_filename.
    for other_file in macro.other_files:
        dst_file = os.path.join(macro_dir, other_file)
        remove_directory_if_empty(os.path.dirname(dst_file))
        os.remove(dst_file)
    return True
Example #6
0
def remove_directory_if_empty(dir_to_remove):
    """Remove the directory if it is empty, with one exception: the directory returned by
    FreeCAD.getUserMacroDir(True) will not be removed even if it is empty."""

    if dir_to_remove == FreeCAD.getUserMacroDir(True):
        return
    if not os.listdir(dir_to_remove):
        os.rmdir(dir_to_remove)
Example #7
0
def update_macro_installation_details(repo) -> None:
    if repo is None or not hasattr(repo, "macro") or repo.macro is None:
        FreeCAD.Console.PrintLog(
            f"Requested macro details for non-macro object\n")
        return
    test_file_one = os.path.join(FreeCAD.getUserMacroDir(True),
                                 repo.macro.filename)
    test_file_two = os.path.join(FreeCAD.getUserMacroDir(True),
                                 "Macro_" + repo.macro.filename)
    if os.path.exists(test_file_one):
        repo.updated_timestamp = os.path.getmtime(test_file_one)
        repo.installed_version = get_macro_version_from_file(test_file_one)
    elif os.path.exists(test_file_two):
        repo.updated_timestamp = os.path.getmtime(test_file_two)
        repo.installed_version = get_macro_version_from_file(test_file_two)
    else:
        return
Example #8
0
def install_macro_to_toolbar(repo: Addon, toolbar: object) -> None:
    """Adds an icon for the given macro to the given toolbar."""
    menuText = repo.display_name
    tooltipText = f"<b>{repo.display_name}</b>"
    if repo.macro.comment:
        tooltipText += f"<br/><p>{repo.macro.comment}</p>"
        whatsThisText = repo.macro.comment
    else:
        whatsThisText = translate(
            "AddonsInstaller",
            "A macro installed with the FreeCAD Addon Manager")
    statustipText = (translate("AddonsInstaller", "Run",
                               "Indicates a macro that can be 'run'") + " " +
                     repo.display_name)
    if repo.macro.icon:
        if os.path.isabs(repo.macro.icon):
            pixmapText = os.path.normpath(repo.macro.icon)
        else:
            macro_repo_dir = FreeCAD.getUserMacroDir(True)
            pixmapText = os.path.normpath(
                os.path.join(macro_repo_dir, repo.macro.icon))
    elif repo.macro.xpm:
        macro_repo_dir = FreeCAD.getUserMacroDir(True)
        icon_file = os.path.normpath(
            os.path.join(macro_repo_dir, repo.macro.name + "_icon.xpm"))
        with open(icon_file, "w", encoding="utf-8") as f:
            f.write(repo.macro.xpm)
        pixmapText = icon_file
    else:
        pixmapText = None

    # Add this command to that toolbar
    command_name = FreeCADGui.Command.createCustomCommand(
        repo.macro.filename,
        menuText,
        tooltipText,
        whatsThisText,
        statustipText,
        pixmapText,
    )
    toolbar.SetString(command_name, "FreeCAD")

    # Force the toolbars to be recreated
    wb = FreeCADGui.activeWorkbench()
    wb.reloadActive()
Example #9
0
def remove_directory_if_empty(dir):
    """Remove the directory if it is empty

    Directory FreeCAD.getUserMacroDir(True) will not be removed even if empty.
    """
    if dir == FreeCAD.getUserMacroDir(True):
        return
    if not os.listdir(dir):
        os.rmdir(dir)
Example #10
0
    def remove(self) -> bool:
        """Remove a macro and all its related files

        Returns True if the macro was removed correctly.
        """

        if not self.is_installed():
            # Macro not installed, nothing to do.
            return True
        macro_dir = FreeCAD.getUserMacroDir(True)
        macro_path = os.path.join(macro_dir, self.filename)
        macro_path_with_macro_prefix = os.path.join(macro_dir,
                                                    "Macro_" + self.filename)
        if os.path.exists(macro_path):
            os.remove(macro_path)
        elif os.path.exists(macro_path_with_macro_prefix):
            os.remove(macro_path_with_macro_prefix)
        # Remove related files, which are supposed to be given relative to
        # self.src_filename.
        if self.xpm:
            xpm_file = os.path.join(macro_dir, self.name + "_icon.xpm")
            if os.path.exists(xpm_file):
                os.remove(xpm_file)
        for other_file in self.other_files:
            if not other_file:
                continue
            FreeCAD.Console.PrintMessage(f"{other_file}...")
            dst_file = os.path.join(macro_dir, other_file)
            if not dst_file or not os.path.exists(dst_file):
                FreeCAD.Console.PrintMessage(f"X\n")
                continue
            try:
                os.remove(dst_file)
                remove_directory_if_empty(os.path.dirname(dst_file))
                FreeCAD.Console.PrintMessage("✓\n")
            except Exception:
                FreeCAD.Console.PrintMessage(f"?\n")
        if os.path.isabs(self.icon):
            dst_file = os.path.normpath(
                os.path.join(macro_dir, os.path.basename(self.icon)))
            if os.path.exists(dst_file):
                try:
                    FreeCAD.Console.PrintMessage(
                        f"{os.path.basename(self.icon)}...")
                    os.remove(dst_file)
                    FreeCAD.Console.PrintMessage("✓\n")
                except Exception:
                    FreeCAD.Console.PrintMessage(f"?\n")
        return True
Example #11
0
    def executemacro(self):
        if self.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.listMacros.currentRow()]
            if not macro.is_installed():
                # Macro not installed, nothing to do.
                return
            macro_path = os.path.join(FreeCAD.getUserMacroDir(True), macro.filename)
            if os.path.exists(macro_path):
                macro_path = macro_path.replace("\\","/")

                FreeCADGui.open(str(macro_path))
                self.hide()
                FreeCADGui.SendMsgToActiveView("Run")
        else:
            self.buttonExecute.setEnabled(False)
Example #12
0
def install_macro(macro, macro_repo_dir):

    """Install a macro and all its related files

    Returns True if the macro was installed correctly.

    Parameters
    ----------
    - macro: an addonmanager_macro.Macro instance
    """

    if not macro.code:
        return False
    macro_dir = FreeCAD.getUserMacroDir(True)
    if not os.path.isdir(macro_dir):
        try:
            os.makedirs(macro_dir)
        except OSError:
            return False
    macro_path = os.path.join(macro_dir, macro.filename)
    if sys.version_info.major < 3:
        # In python2 the code is a bytes object.
        mode = 'wb'
    else:
        mode = 'w'
    try:
        with open(macro_path, mode) as macrofile:
            macrofile.write(macro.code)
    except IOError:
        return False
    # Copy related files, which are supposed to be given relative to
    # macro.src_filename.
    base_dir = os.path.dirname(macro.src_filename)
    for other_file in macro.other_files:
        dst_dir = os.path.join(macro_dir, os.path.dirname(other_file))
        if not os.path.isdir(dst_dir):
            try:
                os.makedirs(dst_dir)
            except OSError:
                return False
        src_file = os.path.join(base_dir, other_file)
        dst_file = os.path.join(macro_dir, other_file)
        try:
            shutil.copy(src_file, dst_file)
        except IOError:
            return False
    return True
    def update_macro(self, repo: Addon):
        """Updating a macro happens in this function, in the current thread"""

        cache_path = os.path.join(
            FreeCAD.getUserCachePath(), "AddonManager", "MacroCache"
        )
        os.makedirs(cache_path, exist_ok=True)
        install_succeeded, _ = repo.macro.install(cache_path)

        if install_succeeded:
            install_succeeded, _ = repo.macro.install(FreeCAD.getUserMacroDir(True))
            utils.update_macro_installation_details(repo)

        if install_succeeded:
            self.success.emit(repo)
        else:
            self.failure.emit(repo)
def install_macro(macro, macro_repo_dir):
    """Install a macro and all its related files

    Returns True if the macro was installed correctly.

    Parameters
    ----------
    - macro: a addonmanager_macro.Macro instance
    """
    if not macro.code:
        return False
    macro_dir = FreeCAD.getUserMacroDir(True)
    if not os.path.isdir(macro_dir):
        try:
            os.makedirs(macro_dir)
        except OSError:
            return False
    macro_path = os.path.join(macro_dir, macro.filename)
    if sys.version_info.major < 3:
        # In python2 the code is a bytes object.
        mode = 'wb'
    else:
        mode = 'w'
    try:
        with open(macro_path, mode) as macrofile:
            macrofile.write(macro.code)
    except IOError:
        return False
    # Copy related files, which are supposed to be given relative to
    # macro.src_filename.
    base_dir = os.path.dirname(macro.src_filename)
    for other_file in macro.other_files:
        dst_dir = os.path.join(base_dir, os.path.dirname(other_file))
        if not os.path.isdir(dst_dir):
            try:
                os.makedirs(dst_dir)
            except OSError:
                return False
        src_file = os.path.join(base_dir, other_file)
        dst_file = os.path.join(macro_dir, other_file)
        try:
            shutil.copy(src_file, dst_file)
        except IOError:
            return False
    return True
    def run_git_clone(self, clonedir: str) -> None:
        """Clones a repo using git"""
        self.status_message.emit("Cloning module...")
        current_thread = QtCore.QThread.currentThread()

        FreeCAD.Console.PrintMessage("Cloning repo...\n")
        if self.repo.git_lock.locked():
            FreeCAD.Console.PrintMessage("Waiting for lock to be released to us...\n")
            if not self.repo.git_lock.acquire(timeout=2):
                FreeCAD.Console.PrintError(
                    "Timeout waiting for a lock on the git process, failed to clone repo\n"
                )
                return
            self.repo.git_lock.release()

        with self.repo.git_lock:
            FreeCAD.Console.PrintMessage("Lock acquired...\n")
            self.git_manager.clone(self.repo.url, clonedir)
            FreeCAD.Console.PrintMessage("Initial clone complete...\n")
            if current_thread.isInterruptionRequested():
                return

            if current_thread.isInterruptionRequested():
                return

            FreeCAD.Console.PrintMessage("Clone complete\n")

        if self.repo.contains_workbench():
            answer = translate(
                "AddonsInstaller",
                "Workbench successfully installed. Please restart FreeCAD to apply the changes.",
            )
        else:
            answer = translate(
                "AddonsInstaller",
                "Addon successfully installed.",
            )

        if self.repo.repo_type == Addon.Kind.WORKBENCH:
            # symlink any macro contained in the module to the macros folder
            macro_dir = FreeCAD.getUserMacroDir(True)
            if not os.path.exists(macro_dir):
                os.makedirs(macro_dir)
            if os.path.exists(clonedir):
                for f in os.listdir(clonedir):
                    if f.lower().endswith(".fcmacro"):
                        try:
                            utils.symlink(
                                os.path.join(clonedir, f), os.path.join(macro_dir, f)
                            )
                        except OSError:
                            # If the symlink failed (e.g. for a non-admin user on Windows), copy
                            # the macro instead
                            shutil.copy(
                                os.path.join(clonedir, f), os.path.join(macro_dir, f)
                            )
                        FreeCAD.ParamGet(
                            "User parameter:Plugins/" + self.repo.name
                        ).SetString("destination", clonedir)
                        # pylint: disable=line-too-long
                        answer += "\n\n" + translate(
                            "AddonsInstaller",
                            "A macro has been installed and is available under Macro -> Macros menu",
                        )
                        answer += ":\n<b>" + f + "</b>"
        self.update_metadata()
        self.success.emit(self.repo, answer)
Example #16
0
 def macroFilePath(cls):
     grp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
     return grp.GetString("MacroPath", FreeCAD.getUserMacroDir())
Example #17
0
    def display_repo_status(self, status):
        repo = self.repo
        self.set_change_branch_button_state()
        if status != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:

            version = repo.installed_version
            date = ""
            installed_version_string = "<h3>"
            if repo.updated_timestamp:
                date = (QDateTime.fromTime_t(
                    repo.updated_timestamp).date().toString(
                        Qt.SystemLocaleShortDate))
            if version and date:
                installed_version_string += (
                    translate("AddonsInstaller",
                              "Version {version} installed on {date}").format(
                                  version=version, date=date) + ". ")
            elif version:
                installed_version_string += (translate(
                    "AddonsInstaller", "Version {version} installed") +
                                             ". ").format(version=version)
            elif date:
                installed_version_string += (
                    translate("AddonsInstaller", "Installed on {date}") +
                    ". ").format(date=date)
            else:
                installed_version_string += (
                    translate("AddonsInstaller", "Installed") + ". ")

            if status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
                if repo.metadata:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller",
                        "On branch {}, update available to version",
                    ).format(repo.branch) + " ")
                    installed_version_string += repo.metadata.Version
                    installed_version_string += ".</b>"
                elif repo.macro and repo.macro.version:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller", "Update available to version") +
                                                 " ")
                    installed_version_string += repo.macro.version
                    installed_version_string += ".</b>"
                else:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller",
                        "An update is available",
                    ) + ".</b>")
            elif status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
                detached_head = False
                branch = repo.branch
                if have_git and repo.repo_type != AddonManagerRepo.RepoType.MACRO:
                    basedir = FreeCAD.getUserAppDataDir()
                    moddir = os.path.join(basedir, "Mod", repo.name)
                    if os.path.exists(os.path.join(moddir, ".git")):
                        gitrepo = git.Repo(moddir)
                        branch = gitrepo.head.ref.name
                        detached_head = gitrepo.head.is_detached

                if detached_head:
                    installed_version_string += (translate(
                        "AddonsInstaller",
                        "Git tag '{}' checked out, no updates possible",
                    ).format(branch) + ".")
                else:
                    installed_version_string += (translate(
                        "AddonsInstaller",
                        "This is the latest version available for branch {}",
                    ).format(branch) + ".")
            elif status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
                installed_version_string += (
                    translate("AddonsInstaller",
                              "Updated, please restart FreeCAD to use") + ".")
            elif status == AddonManagerRepo.UpdateStatus.UNCHECKED:

                pref = FreeCAD.ParamGet(
                    "User parameter:BaseApp/Preferences/Addons")
                autocheck = pref.GetBool("AutoCheck", False)
                if autocheck:
                    installed_version_string += (translate(
                        "AddonsInstaller", "Update check in progress") + ".")
                else:
                    installed_version_string += (
                        translate("AddonsInstaller",
                                  "Automatic update checks disabled") + ".")

            installed_version_string += "</h3>"
            self.ui.labelPackageDetails.setText(installed_version_string)
            if repo.status() == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
                self.ui.labelPackageDetails.setStyleSheet(
                    "color:" + utils.attention_color_string())
            else:
                self.ui.labelPackageDetails.setStyleSheet(
                    "color:" + utils.bright_color_string())
            self.ui.labelPackageDetails.show()

            if repo.macro is not None:
                moddir = FreeCAD.getUserMacroDir(True)
            else:
                basedir = FreeCAD.getUserAppDataDir()
                moddir = os.path.join(basedir, "Mod", repo.name)
            installationLocationString = (
                translate("AddonsInstaller", "Installation location") + ": " +
                moddir)

            self.ui.labelInstallationLocation.setText(
                installationLocationString)
            self.ui.labelInstallationLocation.show()
        else:
            self.ui.labelPackageDetails.hide()
            self.ui.labelInstallationLocation.hide()

        if status == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
            self.ui.buttonInstall.show()
            self.ui.buttonUninstall.hide()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()
        elif status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()
        elif status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.show()
            self.ui.buttonCheckForUpdate.hide()
        elif status == AddonManagerRepo.UpdateStatus.UNCHECKED:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.show()
        elif status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()

        required_version = self.requires_newer_freecad()
        if repo.obsolete:
            self.ui.labelWarningInfo.show()
            self.ui.labelWarningInfo.setText("<h1>" + translate(
                "AddonsInstaller", "WARNING: This addon is obsolete") +
                                             "</h1>")
            self.ui.labelWarningInfo.setStyleSheet(
                "color:" + utils.warning_color_string())
        elif repo.python2:
            self.ui.labelWarningInfo.show()
            self.ui.labelWarningInfo.setText("<h1>" + translate(
                "AddonsInstaller", "WARNING: This addon is Python 2 Only") +
                                             "</h1>")
            self.ui.labelWarningInfo.setStyleSheet(
                "color:" + utils.warning_color_string())
        elif required_version:
            self.ui.labelWarningInfo.show()
            self.ui.labelWarningInfo.setText("<h1>" + translate(
                "AddonsInstaller", "WARNING: This addon requires FreeCAD ") +
                                             required_version + "</h1>")
            self.ui.labelWarningInfo.setStyleSheet(
                "color:" + utils.warning_color_string())

        else:
            self.ui.labelWarningInfo.hide()
Example #18
0
    def __launch(self):
        """This function generates and displays the MultiCopy GUI interface.

        It creates all the dialog boxes for user interaction and input.
        """
        self.main_dialog = gui.PySideUic.loadUi(
            app.getUserMacroDir(True) + '/MultiCopy/resources/MultiCopy_Main_Dialog.ui'
        )
        objects_list_textbox = self.main_dialog.findChild(
            QTextEdit, 'objects_list_textbox'
        )
        commands_input_textbox = self.main_dialog.findChild(
            QPlainTextEdit, 'commands_input_textbox'
        )
        # Adds a filter to detect Paste Code Commands changes in the 'commands_input_textbox' and
        # validate the same.
        _filter = Filter()
        _filter.sendObject(self)
        commands_input_textbox.installEventFilter(_filter)
        # Inserts the list of selected FreeCAD objects into the 'objects_list_textbox'.
        objects_list_textbox_text = '<table>'
        for i_, selected_obj in enumerate(self.selected_objs, 1):
            objects_list_textbox_text += (
                '<tr><td>['
                + str(i_)
                + ']</td><td>&nbsp;&nbsp;'
                + selected_obj.Label
                + '</td><td>&nbsp;&nbsp;&nbsp;&nbsp;&#60;'
                + str(selected_obj.TypeId).replace('\'', '')
                + '&#62;</td></tr>'
            )
        objects_list_textbox_text += '</table>'
        objects_list_textbox.setHtml(objects_list_textbox_text)
        commands_input_textbox.setPlainText('from ')
        commands_input_textbox.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
        self.__numbering_type_radios_clicked()
        for docElem in self.list_of_documents:
            self.main_dialog.findChild(QComboBox, 'documents_list_combobox').addItem(
                app.getDocument(docElem).Label
            )
        self.main_dialog.findChild(
            QRadioButton, 'numbering_type_n_radio'
        ).clicked.connect(lambda: self.__numbering_type_radios_clicked())
        self.main_dialog.findChild(
            QRadioButton, 'numbering_type_ru_radio'
        ).clicked.connect(lambda: self.__numbering_type_radios_clicked())
        self.main_dialog.findChild(
            QRadioButton, 'numbering_type_rl_radio'
        ).clicked.connect(lambda: self.__numbering_type_radios_clicked())
        self.main_dialog.findChild(
            QRadioButton, 'numbering_type_au_radio'
        ).clicked.connect(lambda: self.__numbering_type_radios_clicked())
        self.main_dialog.findChild(
            QRadioButton, 'numbering_type_al_radio'
        ).clicked.connect(lambda: self.__numbering_type_radios_clicked())
        self.main_dialog.findChild(
            QCheckBox, 'delete_selections_check'
        ).toggled.connect(lambda: self.__delete_selections_check_toggled())
        self.main_dialog.findChild(QCheckBox, 'add_separator_check').toggled.connect(
            lambda: self.__add_separator_check_toggled()
        )
        self.main_dialog.findChild(QCheckBox, 'add_padding_check').toggled.connect(
            lambda: self.__add_padding_check_toggled()
        )
        self.main_dialog.findChild(QPushButton, 'paste_button').clicked.connect(
            lambda: self.__paste_button_clicked()
        )
        self.main_dialog.findChild(QTabWidget, 'tabset').currentChanged.connect(
            lambda: self.__tabset_tab_toggled()
        )
        self.main_dialog.findChild(QPushButton, 'command_list_button').clicked.connect(
            launch_commands_list_dialog
        )
        self.main_dialog.findChild(QPushButton, 'close_button').clicked.connect(
            lambda: self.main_dialog.done(1)
        )
        self.main_dialog.setWindowIcon(
            QIcon(app.getUserMacroDir(True) + '/MultiCopy/resources/MultiCopy.svg')
        )
        self.main_dialog.exec_()
    def run(self):

        "installs or updates the selected addon"

        git = None
        try:
            import git
        except Exception as e:
            self.info_label.emit("GitPython not found.")
            print(e)
            FreeCAD.Console.PrintWarning(
                translate(
                    "AddonsInstaller",
                    "GitPython not found. Using standard download instead.") +
                "\n")
            try:
                import zipfile
            except:
                self.info_label.emit("no zip support.")
                FreeCAD.Console.PrintError(
                    translate(
                        "AddonsInstaller",
                        "Your version of python doesn't appear to support ZIP files. Unable to proceed."
                    ) + "\n")
                return
            try:
                import StringIO as io
            except ImportError:  # StringIO is not available with python3
                import io
        if not isinstance(self.idx, list):
            self.idx = [self.idx]
        for idx in self.idx:
            if idx < 0:
                return
            if not self.repos:
                return
            if NOGIT:
                git = None
            basedir = FreeCAD.getUserAppDataDir()
            moddir = basedir + os.sep + "Mod"
            if not os.path.exists(moddir):
                os.makedirs(moddir)
            clonedir = moddir + os.sep + self.repos[idx][0]
            self.progressbar_show.emit(True)
            if os.path.exists(clonedir):
                self.info_label.emit("Updating module...")
                if git:
                    if not os.path.exists(clonedir + os.sep + '.git'):
                        # Repair addon installed with raw download
                        bare_repo = git.Repo.clone_from(self.repos[idx][1],
                                                        clonedir + os.sep +
                                                        '.git',
                                                        bare=True)
                        try:
                            with bare_repo.config_writer() as cw:
                                cw.set('core', 'bare', False)
                        except AttributeError:
                            FreeCAD.Console.PrintWarning(
                                translate(
                                    "AddonsInstaller",
                                    "Outdated GitPython detected, consider upgrading with pip."
                                ) + "\n")
                            cw = bare_repo.config_writer()
                            cw.set('core', 'bare', False)
                            del cw
                        repo = git.Repo(clonedir)
                        repo.head.reset('--hard')
                    repo = git.Git(clonedir)
                    try:
                        answer = repo.pull()
                    except:
                        print("Error updating module", self.repos[idx][1],
                              " - Please fix manually")
                        answer = repo.status()
                        print(answer)
                    else:
                        # Update the submodules for this repository
                        repo_sms = git.Repo(clonedir)
                        for submodule in repo_sms.submodules:
                            submodule.update(init=True, recursive=True)
                else:
                    answer = self.download(self.repos[idx][1], clonedir)
            else:
                self.info_label.emit("Checking module dependencies...")
                depsok, answer = self.checkDependencies(self.repos[idx][1])
                if depsok:
                    if git:
                        self.info_label.emit("Cloning module...")
                        repo = git.Repo.clone_from(self.repos[idx][1],
                                                   clonedir,
                                                   branch='master')

                        # Make sure to clone all the submodules as well
                        if repo.submodules:
                            repo.submodule_update(recursive=True)
                    else:
                        self.info_label.emit("Downloading module...")
                        self.download(self.repos[idx][1], clonedir)
                    answer = translate(
                        "AddonsInstaller",
                        "Workbench successfully installed. Please restart FreeCAD to apply the changes."
                    )
            # symlink any macro contained in the module to the macros folder
            macro_dir = FreeCAD.getUserMacroDir(True)
            if not os.path.exists(macro_dir):
                os.makedirs(macro_dir)
            if os.path.exists(clonedir):
                for f in os.listdir(clonedir):
                    if f.lower().endswith(".fcmacro"):
                        print("copying macro:", f)
                        utils.symlink(os.path.join(clonedir, f),
                                      os.path.join(macro_dir, f))
                        FreeCAD.ParamGet('User parameter:Plugins/' +
                                         self.repos[idx][0]).SetString(
                                             "destination", clonedir)
                        answer += "\n\n" + translate(
                            "AddonsInstaller",
                            "A macro has been installed and is available under Macro -> Macros menu"
                        ) + ":"
                        answer += "\n<b>" + f + "</b>"
            self.progressbar_show.emit(False)
            self.info_label.emit(answer)
            self.mark_recompute.emit(self.repos[idx][0])
        self.stop = True
Example #20
0
def processFileNameSubstitutions(
    job,
    subpartname,
    sequencenumber,
    outputpath,
    filename,
    ext,
):
    """Process any substitutions in the outputpath or filename."""

    # The following section allows substitution within the path part
    PathLog.track(f"path before substitution: {outputpath}")

    if "%D" in outputpath:  # Directory of active document
        D = FreeCAD.ActiveDocument.FileName
        if D:
            D = os.path.dirname(D)
            # in case the document is in the current working directory
            if not D:
                D = "."
        else:
            FreeCAD.Console.PrintError(
                "Please save document in order to resolve output path!\n"
            )
            return None
        outputpath = outputpath.replace("%D", D)

    if "%M" in outputpath:
        M = FreeCAD.getUserMacroDir()
        outputpath = outputpath.replace("%M", M)

    # Use the file label
    if "%d" in outputpath:
        d = FreeCAD.ActiveDocument.Label
        outputpath = outputpath.replace("%d", d)

    # Use the name of the active job object
    if "%j" in outputpath:
        j = job.Label
        outputpath = outputpath.replace("%j", j)

    PathLog.track(f"path after substitution: {outputpath}")

    # The following section allows substitution within the filename part
    PathLog.track(f"filename before substitution: {filename}")

    # Use the file label
    if "%d" in filename:
        d = FreeCAD.ActiveDocument.Label
        filename = filename.replace("%d", d)

    # Use the name of the active job object
    if "%j" in filename:
        j = job.Label
        filename = filename.replace("%j", j)

    # Use the sequence number if explicitly called
    if "%S" in filename:
        j = job.Label
        filename = filename.replace("%S", str(sequencenumber))

    # This section handles unique names for splitting output
    if job.SplitOutput:
        PathLog.track()
        if "%T" in filename and job.OrderOutputBy == "Tool":
            filename = filename.replace("%T", subpartname)

        if "%t" in filename and job.OrderOutputBy == "Tool":
            filename = filename.replace("%t", subpartname)

        if "%W" in filename and job.OrderOutputBy == "Fixture":
            filename = filename.replace("%W", subpartname)

        if "%O" in filename and job.OrderOutputBy == "Operation":
            filename = filename.replace("%O", subpartname)

        if (
            "%S" in filename
        ):  # We always add a sequence number but the user can say where
            filename = filename.replace("%S", str(sequencenumber))
        else:
            filename = f"{filename}-{sequencenumber}"

    PathLog.track(f"filename after substitution: {filename}")

    if not ext:
        ext = ".nc"
    PathLog.track(f"file extension: {ext}")

    fullPath = f"{outputpath}{os.path.sep}{filename}{ext}"

    PathLog.track(f"full filepath: {fullPath}")
    return fullPath
Example #21
0
def get_macro_path():
    """Returns platform independent macro base path"""
    return Path(App.getUserMacroDir(True))
    def run(self):
        "installs or updates the selected addon"
        git = None
        try:
            import git
        except Exception as e:
            self.info_label.emit("GitPython not found.")
            print(e)
            FreeCAD.Console.PrintWarning(translate("AddonsInstaller","GitPython not found. Using standard download instead.")+"\n")
            try:
                import zipfile
            except:
                self.info_label.emit("no zip support.")
                FreeCAD.Console.PrintError(translate("AddonsInstaller","Your version of python doesn't appear to support ZIP files. Unable to proceed.")+"\n")
                return
            try:
                import StringIO as io
            except ImportError: # StringIO is not available with python3
                import io
        if not isinstance(self.idx,list):
            self.idx = [self.idx]
        for idx in self.idx:
            if idx < 0:
                return
            if not self.repos:
                return
            if NOGIT:
                git = None
            basedir = FreeCAD.getUserAppDataDir()
            moddir = basedir + os.sep + "Mod"
            if not os.path.exists(moddir):
                os.makedirs(moddir)
            clonedir = moddir + os.sep + self.repos[idx][0]
            self.progressbar_show.emit(True)
            if os.path.exists(clonedir):
                self.info_label.emit("Updating module...")
                if git:
                    if not os.path.exists(clonedir + os.sep + '.git'):
                        # Repair addon installed with raw download
                        bare_repo = git.Repo.clone_from(self.repos[idx][1], clonedir + os.sep + '.git', bare=True)
                        try:
                            with bare_repo.config_writer() as cw:
                                cw.set('core', 'bare', False)
                        except AttributeError:
                            FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
                            cw = bare_repo.config_writer()
                            cw.set('core', 'bare', False)
                            del cw
                        repo = git.Repo(clonedir)
                        repo.head.reset('--hard')
                    repo = git.Git(clonedir)
                    answer = repo.pull()

                    # Update the submodules for this repository
                    repo_sms = git.Repo(clonedir)
                    for submodule in repo_sms.submodules:
                        submodule.update(init=True, recursive=True)
                else:
                    answer = self.download(self.repos[idx][1],clonedir)
            else:
                self.info_label.emit("Checking module dependencies...")
                depsok,answer = self.checkDependencies(self.repos[idx][1])
                if depsok:
                    if git:
                        self.info_label.emit("Cloning module...")
                        repo = git.Repo.clone_from(self.repos[idx][1], clonedir, branch='master')

                        # Make sure to clone all the submodules as well
                        if repo.submodules:
                            repo.submodule_update(recursive=True)
                    else:
                        self.info_label.emit("Downloading module...")
                        self.download(self.repos[idx][1],clonedir)
                    answer = translate("AddonsInstaller", "Workbench successfully installed. Please restart FreeCAD to apply the changes.")
            # symlink any macro contained in the module to the macros folder
            macro_dir = FreeCAD.getUserMacroDir(True)
            if not os.path.exists(macro_dir):
                os.makedirs(macro_dir)
            for f in os.listdir(clonedir):
                if f.lower().endswith(".fcmacro"):
                    print("copying macro:",f)
                    symlink(os.path.join(clonedir, f), os.path.join(macro_dir, f))
                    FreeCAD.ParamGet('User parameter:Plugins/'+self.repos[idx][0]).SetString("destination",clonedir)
                    answer += translate("AddonsInstaller", "A macro has been installed and is available the Macros menu") + ": <b>"
                    answer += f + "</b>"
            self.progressbar_show.emit(False)
            self.info_label.emit(answer)
        self.stop = True
Example #23
0
    def setUI(self):

        self.base = QtGui.QWidget()
        self.base.setWindowTitle("PACE tools")

        self.form = self.base
        self.layout = QtGui.QVBoxLayout()
        self.form.setLayout(self.layout)

        titles = {'init': 'Initial situation', 'mod': 'Modified situation'}

        iconPath = os.path.join(App.getUserMacroDir(True), 'pace_logo.png')
        print(iconPath)

        if os.path.exists(iconPath) and os.name != 'posix':
            print("In if")
            paceIcon = QtGui.QIcon(iconPath)
            self.base.setWindowIcon(paceIcon)  #dommage, ca fait crasher...

        self.situationChoice = QtGui.QComboBox()
        self.situationChoice.addItems(
            [titles[situation] for situation in self.proj.VP.keys()])
        self.situationChoice.currentIndexChanged.connect(self.change)

        skinElementsDescriptionButton = QtGui.QPushButton(
            "Skin elements description")
        #skinElementsDescriptionButton.clicked.connect(self.proj.setSkinElementsDescription)
        skinElementsDescriptionButton.clicked.connect(
            lambda: paceGeomTools.skinElementsConfigurator(self.proj))

        self.compass = paceGeomTools.Compass()

        if (hasattr(self.proj, 'sectormap')):
            self.compass.sectormap = self.proj.sectormap

        setCompassButton = QtGui.QPushButton("Define building orientation")
        setCompassButton.clicked.connect(
            lambda: self.compass.setCompassFromFace(self.proj))

        exportToPaceButton = QtGui.QPushButton("Export geometry to PACE")
        exportToPaceButton.clicked.connect(self.proj.exportToPace)

        self.collayouts = {}
        self.vpwidgets = {}

        for situation in self.proj.VP.keys():

            self.vpwidgets[situation] = QtGui.QWidget()
            self.collayouts[situation] = QtGui.QVBoxLayout()

            identifySelB = QtGui.QPushButton("Assign SELECTION wall type")
            identifySelB.clicked.connect(
                self.proj.VP[situation].setSelectionType)

            #showlabelsB=QtGui.QPushButton("Show/refresh wall types")
            #showlabelsB.clicked.connect(self.proj.VP[situation].showVisibleFacesLabel)

            #hidelabelsB=QtGui.QPushButton("Hide wall types")
            #hidelabelsB.clicked.connect(self.proj.VP[situation].hideAllLabels)

            colorbyLabelB = QtGui.QPushButton("Color by Label")
            colorbyLabelB.clicked.connect(self.proj.VP[situation].colorByLabel)

            showHideLegend = QtGui.QPushButton("Show/hide color legend")
            showHideLegend.clicked.connect(self.showHideLegend)

            #hideL=QtGui.QPushButton("Hide colors legend")
            #hideL.clicked.connect(self.proj.VP[situation].hideLegend)

            showAreasAndVolume = QtGui.QPushButton("Show areas per wall type")
            showAreasAndVolume.clicked.connect(
                self.proj.VP[situation].showAreasAndVolume)

            showAreasWt = QtGui.QPushButton(
                "Show areas per wall type and Facade")
            showAreasWt.clicked.connect(
                lambda state=False, situation=situation: self.proj.VP[
                    situation].showAreasPerFacade(self.proj.sectormap))
            # see https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt
            # I cant avoid lambda if I want to pass a parameter
            # However, the lambda does not record the situation value, but only the reference !
            # Have to use default value (situation = situation) to force recording it

            exportStep = QtGui.QPushButton("Export step file")
            exportStep.clicked.connect(self.proj.VP[situation].exportStep)

            exportPNG = QtGui.QPushButton("Save PNG figure to file")
            exportPNG.clicked.connect(self.proj.VP[situation].exportPNG)

            exportPNGtoPACE = QtGui.QPushButton(
                "Export current view to PACE file (" + situation +
                " situation)")
            exportPNGtoPACE.clicked.connect(
                lambda state=False, situation=situation: self.proj.
                insertCurrentViewInPaceFile(situation))

            updateAfterBodyModificationButton = QtGui.QPushButton(
                "Update after body modif")
            updateAfterBodyModificationButton.clicked.connect(
                lambda state=False, situation=situation: self.updateSituation(
                    situation))

            self.collayouts[situation].addWidget(identifySelB)

            #self.collayouts[situation].addWidget(showlabelsB)
            #self.collayouts[situation].addWidget(hidelabelsB)

            self.collayouts[situation].addWidget(colorbyLabelB)

            self.collayouts[situation].addWidget(showHideLegend)
            # self.collayouts[situation].addWidget(hideL)

            self.collayouts[situation].addWidget(showAreasAndVolume)
            self.collayouts[situation].addWidget(showAreasWt)

            self.collayouts[situation].addWidget(exportStep)
            self.collayouts[situation].addWidget(exportPNG)
            self.collayouts[situation].addWidget(exportPNGtoPACE)

            self.collayouts[situation].addWidget(
                updateAfterBodyModificationButton)

            self.vpwidgets[situation].setLayout(self.collayouts[situation])

        if ("mod" in self.proj.VP.keys()):

            mapB = QtGui.QPushButton("Copy types from init to mod")
            mapB.clicked.connect(
                lambda: self.proj.copyLabelsToOtherSituation('init', 'mod'))
            self.collayouts['mod'].addWidget(mapB)

        self.layout.addWidget(self.situationChoice)
        self.layout.addWidget(skinElementsDescriptionButton)
        self.layout.addWidget(setCompassButton)
        self.layout.addWidget(exportToPaceButton)

        self.layout.addWidget(self.vpwidgets['init'])

        if ("mod" in self.proj.VP.keys()):

            self.layout.addWidget(self.vpwidgets['mod'])
            self.vpwidgets['mod'].hide()
Example #24
0
 def is_installed(self):
     if self.on_git and not self.src_filename:
         return False
     return (os.path.exists(os.path.join(FreeCAD.getUserMacroDir(True), self.filename))
             or os.path.exists(os.path.join(FreeCAD.getUserMacroDir(True), 'Macro_' + self.filename)))
Example #25
0
    def show_repo(self, repo: AddonManagerRepo, reload: bool = False) -> None:

        self.repo = repo

        if self.worker is not None:
            if not self.worker.isFinished():
                self.worker.requestInterruption()
                self.worker.wait()

        # Always load bare macros from scratch, we need to grab their code, which isn't cached
        force_reload = reload
        if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
            force_reload = True

        self.check_and_clean_cache(force_reload)

        if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
            self.show_macro(repo)
            self.ui.buttonExecute.show()
        elif repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
            self.show_workbench(repo)
            self.ui.buttonExecute.hide()
        elif repo.repo_type == AddonManagerRepo.RepoType.PACKAGE:
            self.show_package(repo)
            self.ui.buttonExecute.hide()

        if repo.update_status != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:

            version = repo.installed_version
            date = ""
            installed_version_string = "<h3>"
            if repo.updated_timestamp:
                date = (QDateTime.fromTime_t(
                    repo.updated_timestamp).date().toString(
                        Qt.SystemLocaleShortDate))
            if version and date:
                installed_version_string += (
                    translate("AddonsInstaller",
                              f"Version {version} installed on {date}") + ". ")
            elif version:
                installed_version_string += (translate(
                    "AddonsInstaller", f"Version {version} installed") + ". ")
            elif date:
                installed_version_string += (
                    translate("AddonsInstaller", f"Installed on {date}") +
                    ". ")
            else:
                installed_version_string += (
                    translate("AddonsInstaller", "Installed") + ". ")

            if repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
                if repo.metadata:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller", "Update available to version") +
                                                 " ")
                    installed_version_string += repo.metadata.Version
                    installed_version_string += ".</b>"
                elif repo.macro and repo.macro.version:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller", "Update available to version") +
                                                 " ")
                    installed_version_string += repo.macro.version
                    installed_version_string += ".</b>"
                else:
                    installed_version_string += ("<b>" + translate(
                        "AddonsInstaller",
                        "An update is available",
                    ) + ".</b>")
            elif (repo.update_status ==
                  AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE):
                installed_version_string += (
                    translate("AddonsInstaller",
                              "This is the latest version available") + ".")
            elif repo.update_status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
                installed_version_string += (
                    translate("AddonsInstaller",
                              "Updated, please restart FreeCAD to use") + ".")
            elif repo.update_status == AddonManagerRepo.UpdateStatus.UNCHECKED:

                pref = FreeCAD.ParamGet(
                    "User parameter:BaseApp/Preferences/Addons")
                autocheck = pref.GetBool("AutoCheck", False)
                if autocheck:
                    installed_version_string += (translate(
                        "AddonsInstaller", "Update check in progress") + ".")
                else:
                    installed_version_string += (
                        translate("AddonsInstaller",
                                  "Automatic update checks disabled") + ".")

            installed_version_string += "</h3>"
            self.ui.labelPackageDetails.setText(installed_version_string)
            if repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
                self.ui.labelPackageDetails.setStyleSheet(
                    "color:" + utils.attention_color_string())
            else:
                self.ui.labelPackageDetails.setStyleSheet(
                    "color:" + utils.bright_color_string())
            self.ui.labelPackageDetails.show()

            if repo.macro is not None:
                moddir = FreeCAD.getUserMacroDir(True)
            else:
                basedir = FreeCAD.getUserAppDataDir()
                moddir = os.path.join(basedir, "Mod", repo.name)
            installationLocationString = (
                translate("AddonsInstaller", "Installation location") + ": " +
                moddir)

            self.ui.labelInstallationLocation.setText(
                installationLocationString)
            self.ui.labelInstallationLocation.show()
        else:
            self.ui.labelPackageDetails.hide()
            self.ui.labelInstallationLocation.hide()

        if repo.update_status == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
            self.ui.buttonInstall.show()
            self.ui.buttonUninstall.hide()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()
        elif repo.update_status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()
        elif repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.show()
            self.ui.buttonCheckForUpdate.hide()
        elif repo.update_status == AddonManagerRepo.UpdateStatus.UNCHECKED:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.show()
        elif repo.update_status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
            self.ui.buttonInstall.hide()
            self.ui.buttonUninstall.show()
            self.ui.buttonUpdate.hide()
            self.ui.buttonCheckForUpdate.hide()

        if repo.obsolete:
            self.ui.labelWarningInfo.show()
            self.ui.labelWarningInfo.setText("<h1>" + translate(
                "AddonsInstaller", "WARNING: This addon is obsolete") +
                                             "</h1>")
            self.ui.labelWarningInfo.setStyleSheet(
                "color:" + utils.warning_color_string())
        elif repo.python2:
            self.ui.labelWarningInfo.show()
            self.ui.labelWarningInfo.setText("<h1>" + translate(
                "AddonsInstaller", "WARNING: This addon is Python 2 Only") +
                                             "</h1>")
            self.ui.labelWarningInfo.setStyleSheet(
                "color:" + utils.warning_color_string())
        else:
            self.ui.labelWarningInfo.hide()
Example #26
0
from PySide.QtGui import QDialog, QMessageBox, QTableWidgetItem
from femmesh.gmshtools import GmshTools
from femtools import ccxtools
import FreeCAD,FreeCADGui,Part
import PySide
import json
import time

MSGBOX_TITLE = "Iterative CCX Solver"
FEMITERATE_VERSION = "0.0.1"

TYPEID_FEM_MESH = "Fem::FemMeshObjectPython"
TYPEID_FEM_ANALYSIS = "Fem::FemAnalysis"
TYPEID_FEM_SOLVER = "Fem::FemSolverObjectPython"

UI_BASE_PATH = FreeCAD.getUserMacroDir() + "FEMIterate"
UI_MAIN_FILE_PATH = f"{UI_BASE_PATH}/main.ui"
UI_CHANGE_FILE_PATH = f"{UI_BASE_PATH}/addchange.ui"
UI_CHECK_FILE_PATH = f"{UI_BASE_PATH}/addcheck.ui"

GUI_IN_SIDEBAR = True

USERTYPE_NUMBER = "Number"
USERTYPE_UNIT = "Unit"
USERTYPE_PYTHON = "Python expr."

BUILTIN_QUICK_EXPRESSIONS = [
    "max(r.vonMises) < 100",
    "max(r.Temperature) < 300"
]
Example #27
0
class TestOutputNameSubstitution(unittest.TestCase):
    """
    String substitution allows the following:
    %D ... directory of the active document
    %d ... name of the active document (with extension)
    %M ... user macro directory
    %j ... name of the active Job object


    The Following can be used if output is being split. If Output is not split
    these will be ignored.
    %S ... Sequence Number (default)

    %T ... Tool Number
    %t ... Tool Controller label

    %W ... Work Coordinate System
    %O ... Operation Label

        self.job.Fixtures = ["G54"]
        self.job.SplitOutput = False
        self.job.OrderOutputBy = "Fixture"

    Assume:
    active document: self.assertTrue(filename, f"{home}/testdoc.fcstd
    user macro: ~/.local/share/FreeCAD/Macro
    Job:  MainJob
    Operations:
        OutsideProfile
        DrillAllHoles
    TC: 7/16" two flute  (5)
    TC: Drill (2)
    Fixtures: (G54, G55)

    Strings should be sanitized like this to ensure valid filenames
    # import re
    # filename="TC: 7/16" two flute"
    # >>> re.sub(r"[^\w\d-]","_",filename)
    # "TC__7_16__two_flute"

    """

    testfile = FreeCAD.getHomePath(
    ) + "Mod/Path/PathTests/test_filenaming.fcstd"
    testfilepath, testfilename = os.path.split(testfile)
    testfilename, ext = os.path.splitext(testfilename)

    doc = FreeCAD.open(testfile)
    job = doc.getObjectsByLabel("MainJob")[0]
    macro = FreeCAD.getUserMacroDir()

    def test000(self):
        # Test basic name generation with empty string
        FreeCAD.setActiveDocument(self.doc.Label)
        teststring = ""
        self.job.PostProcessorOutputFile = teststring
        self.job.SplitOutput = False
        outlist = PathPost.buildPostList(self.job)

        self.assertTrue(len(outlist) == 1)
        subpart, objs = outlist[0]

        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, f"{self.testfilename}.nc")

    def test015(self):
        # Test basic string substitution without splitting
        teststring = "~/Desktop/%j.nc"
        self.job.PostProcessorOutputFile = teststring
        self.job.SplitOutput = False
        outlist = PathPost.buildPostList(self.job)

        self.assertTrue(len(outlist) == 1)
        subpart, objs = outlist[0]

        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "~/Desktop/MainJob.nc")

    def test010(self):
        # Substitute current file path
        teststring = "%D/testfile.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, f"{self.testfilepath}/testfile.nc")

    def test020(self):
        teststring = "%d.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, f"{self.testfilename}.nc")

    def test030(self):
        teststring = "%M/outfile.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, f"{self.macro}outfile.nc")

    def test040(self):
        # unused substitution strings should be ignored
        teststring = "%d%T%t%W%O/testdoc.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, f"{self.testfilename}/testdoc.nc")

    def test050(self):
        # explicitly using the sequence number should include it where indicated.
        teststring = "%S-%d.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "0-test_filenaming.nc")

    def test060(self):
        # # Split by Tool
        self.job.SplitOutput = True
        self.job.OrderOutputBy = "Tool"
        outlist = PathPost.buildPostList(self.job)

        # substitute jobname and use default sequence numbers
        teststring = "%j.nc"
        self.job.PostProcessorOutputFile = teststring
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "MainJob-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "MainJob-1.nc")

        # Use Toolnumbers and default sequence numbers
        teststring = "%T.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "5-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "2-1.nc")

        # Use Tooldescriptions and default sequence numbers
        teststring = "%t.nc"
        self.job.PostProcessorOutputFile = teststring
        outlist = PathPost.buildPostList(self.job)
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "TC__7_16__two_flute-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "TC__Drill-1.nc")

    def test070(self):
        # Split by WCS
        self.job.SplitOutput = True
        self.job.OrderOutputBy = "Fixture"
        outlist = PathPost.buildPostList(self.job)

        teststring = "%j.nc"
        self.job.PostProcessorOutputFile = teststring
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "MainJob-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "MainJob-1.nc")

        teststring = "%W-%j.nc"
        self.job.PostProcessorOutputFile = teststring
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "G54-MainJob-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "G55-MainJob-1.nc")

    def test080(self):
        # Split by Operation
        self.job.SplitOutput = True
        self.job.OrderOutputBy = "Operation"
        outlist = PathPost.buildPostList(self.job)

        teststring = "%j.nc"
        self.job.PostProcessorOutputFile = teststring
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "MainJob-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "MainJob-1.nc")

        teststring = "%O-%j.nc"
        self.job.PostProcessorOutputFile = teststring
        subpart, objs = outlist[0]
        filename = PathPost.resolveFileName(self.job, subpart, 0)
        self.assertEqual(filename, "OutsideProfile-MainJob-0.nc")
        subpart, objs = outlist[1]
        filename = PathPost.resolveFileName(self.job, subpart, 1)
        self.assertEqual(filename, "DrillAllHoles-MainJob-1.nc")
Example #28
0
def macroFilePath():
    grp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
    return grp.GetString("MacroPath", FreeCAD.getUserMacroDir())
Example #29
0
def resolveFileName(job, subpartname, sequencenumber):
    PathLog.track(subpartname, sequencenumber)

    validPathSubstitutions = ["D", "d", "M", "j"]
    validFilenameSubstitutions = ["j", "d", "T", "t", "W", "O", "S"]

    # Look for preference default
    outputpath, filename = os.path.split(PathPreferences.defaultOutputFile())
    filename, ext = os.path.splitext(filename)

    # Override with document default if it exists
    if job.PostProcessorOutputFile:
        matchstring = job.PostProcessorOutputFile
        candidateOutputPath, candidateFilename = os.path.split(matchstring)

        if candidateOutputPath:
            outputpath = candidateOutputPath

        if candidateFilename:
            filename, ext = os.path.splitext(candidateFilename)

    # Strip any invalid substitutions from the ouputpath
    for match in re.findall("%(.)", outputpath):
        if match not in validPathSubstitutions:
            outputpath = outputpath.replace(f"%{match}", "")

    # if nothing else, use current directory
    if not outputpath:
        outputpath = "."

    # Strip any invalid substitutions from the filename
    for match in re.findall("%(.)", filename):
        if match not in validFilenameSubstitutions:
            filename = filename.replace(f"%{match}", "")

    # if no filename, use the active document label
    if not filename:
        filename = FreeCAD.ActiveDocument.Label

    # if no extension, use something sensible
    if not ext:
        ext = ".nc"

    # By now we should have a sanitized path, filename and extension to work with
    PathLog.track(f"path: {outputpath} name: {filename} ext: {ext}")

    # The following section allows substitution within the path part
    PathLog.track(f"path before substitution: {outputpath}")

    if "%D" in outputpath:  # Directory of active document
        D = FreeCAD.ActiveDocument.FileName
        if D:
            D = os.path.dirname(D)
            # in case the document is in the current working directory
            if not D:
                D = "."
        else:
            FreeCAD.Console.PrintError(
                "Please save document in order to resolve output path!\n"
            )
            return None
        outputpath = outputpath.replace("%D", D)

    if "%M" in outputpath:
        M = FreeCAD.getUserMacroDir()
        outputpath = outputpath.replace("%M", M)

    # Use the file label
    if "%d" in outputpath:
        d = FreeCAD.ActiveDocument.Label
        outputpath = outputpath.replace("%d", d)

    # Use the name of the active job object
    if "%j" in outputpath:
        j = job.Label
        outputpath = outputpath.replace("%j", j)

    PathLog.track(f"path after substitution: {outputpath}")

    # The following section allows substitution within the filename part
    PathLog.track(f"filename before substitution: {filename}")

    # Use the file label
    if "%d" in filename:
        d = FreeCAD.ActiveDocument.Label
        filename = filename.replace("%d", d)

    # Use the name of the active job object
    if "%j" in filename:
        j = job.Label
        filename = filename.replace("%j", j)

    # Use the sequnce number if explicitly called
    if "%S" in filename:
        j = job.Label
        filename = filename.replace("%S", str(sequencenumber))

    # This section handles unique names for splitting output
    if job.SplitOutput:
        PathLog.track()
        if "%T" in filename and job.OrderOutputBy == "Tool":
            filename = filename.replace("%T", subpartname)

        if "%t" in filename and job.OrderOutputBy == "Tool":
            filename = filename.replace("%t", subpartname)

        if "%W" in filename and job.OrderOutputBy == "Fixture":
            filename = filename.replace("%W", subpartname)

        if "%O" in filename and job.OrderOutputBy == "Operation":
            filename = filename.replace("%O", subpartname)

        if (
            "%S" in filename
        ):  # We always add a sequence number but the user can say where
            filename = filename.replace("%S", str(sequencenumber))
        else:
            filename = f"{filename}-{sequencenumber}"

    PathLog.track(f"filename after substitution: {filename}")

    if not ext:
        ext = ".nc"
    PathLog.track(f"file extension: {ext}")

    fullPath = f"{outputpath}{os.path.sep}{filename}{ext}"

    PathLog.track(f"full filepath: {fullPath}")

    # This section determines whether user interaction is necessary
    policy = PathPreferences.defaultOutputPolicy()

    openDialog = policy == "Open File Dialog"
    # if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
    #     # Either the entire filename resolves into a directory or the parent directory doesn't exist.
    #     # Either way I don't know what to do - ask for help
    #     openDialog = True

    if not FreeCAD.GuiUp:  # if testing, or scripting, never open dialog.
        policy = "Append Unique ID on conflict"
        openDialog = False

    if os.path.isfile(fullPath) and not openDialog:
        if policy == "Open File Dialog on conflict":
            openDialog = True
        elif policy == "Append Unique ID on conflict":
            fn, ext = os.path.splitext(fullPath)
            nr = fn[-3:]
            n = 1
            if nr.isdigit():
                n = int(nr)
            while os.path.isfile("%s%03d%s" % (fn, n, ext)):
                n = n + 1
            fullPath = "%s%03d%s" % (fn, n, ext)

    if openDialog:
        foo = QtGui.QFileDialog.getSaveFileName(
            QtGui.QApplication.activeWindow(), "Output File", filename
        )
        if foo[0]:
            fullPath = foo[0]
        else:
            fullPath = None

    # remove any unused substitution strings:
    for s in validPathSubstitutions + validFilenameSubstitutions:
        fullPath = fullPath.replace(f"%{s}", "")

    fullPath = os.path.normpath(fullPath)
    PathLog.track(fullPath)
    return fullPath