예제 #1
0
    def download(self, baseurl, clonedir):
        "downloads and unzip a zip version from a git repo"

        bakdir = None
        if os.path.exists(clonedir):
            bakdir = clonedir+".bak"
            if os.path.exists(bakdir):
                shutil.rmtree(bakdir)
            os.rename(clonedir, bakdir)
        os.makedirs(clonedir)
        zipurl = utils.get_zip_url(baseurl)
        if not zipurl:
            return translate("AddonsInstaller", "Error: Unable to locate zip from") + " " + baseurl
        try:
            print("Downloading "+zipurl)
            u = utils.urlopen(zipurl)
        except Exception:
            return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
        if not u:
            return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
        zfile = _stringio()
        zfile.write(u.read())
        zfile = zipfile.ZipFile(zfile)
        master = zfile.namelist()[0]  # github will put everything in a subfolder
        zfile.extractall(clonedir)
        u.close()
        zfile.close()
        for filename in os.listdir(clonedir+os.sep+master):
            shutil.move(clonedir+os.sep+master+os.sep+filename, clonedir+os.sep+filename)
        os.rmdir(clonedir+os.sep+master)
        if bakdir:
            shutil.rmtree(bakdir)
        return translate("AddonsInstaller", "Successfully installed") + " " + zipurl
예제 #2
0
 def run(self):
     "populates the list of addons"
     self.progressbar_show.emit(True)
     u = urlopen("https://github.com/FreeCAD/FreeCAD-addons")
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode("utf-8")
     u.close()
     p = p.replace("\n"," ")
     p = re.findall("octicon-file-submodule(.*?)message",p)
     basedir = FreeCAD.getUserAppDataDir()
     moddir = basedir + os.sep + "Mod"
     repos = []
     for l in p:
         #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
         name = re.findall("title=\"(.*?) @",l)[0]
         self.info_label.emit(name)
         #url = re.findall("title=\"(.*?) @",l)[0]
         url = "https://github.com/" + re.findall("href=\"\/(.*?)\/tree",l)[0]
         addondir = moddir + os.sep + name
         #print ("found:",name," at ",url)
         if not os.path.exists(addondir):
             state = 0
         else:
             state = 1
         repos.append([name,url,state])
     if not repos:
         self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
     else:
         repos = sorted(repos, key=lambda s: s[0].lower())
         for repo in repos:
             self.addon_repo.emit(repo)
         self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated."))
     self.progressbar_show.emit(False)
     self.stop = True
예제 #3
0
 def download(self,giturl,clonedir):
     "downloads and unzip from github"
     import zipfile
     bakdir = None
     if os.path.exists(clonedir):
         bakdir = clonedir+".bak"
         if os.path.exists(bakdir):
             shutil.rmtree(bakdir)
         os.rename(clonedir,bakdir)
     os.makedirs(clonedir)
     zipurl = giturl+"/archive/master.zip"
     try:
         print("Downloading "+zipurl)
         u = urlopen(zipurl)
     except:
         return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
     zfile = _stringio()
     zfile.write(u.read())
     zfile = zipfile.ZipFile(zfile)
     master = zfile.namelist()[0] # github will put everything in a subfolder
     zfile.extractall(clonedir)
     u.close()
     zfile.close()
     for filename in os.listdir(clonedir+os.sep+master):
         shutil.move(clonedir+os.sep+master+os.sep+filename, clonedir+os.sep+filename)
     os.rmdir(clonedir+os.sep+master)
     if bakdir:
         shutil.rmtree(bakdir)
     return translate("AddonsInstaller", "Successfully installed") + " " + zipurl
예제 #4
0
 def install(self,repos=None):
     if self.tabWidget.currentIndex() == 0:
         # Tab "Workbenches".
         idx = None
         if repos:
             idx = []
             for repo in repos:
                 for i,r in enumerate(self.repos):
                     if r[0] == repo:
                         idx.append(i)
         else:
             idx = self.listWorkbenches.currentRow()
         if idx != None:
             if hasattr(self,"install_worker"):
                 if self.install_worker.isRunning():
                     return
             self.install_worker = InstallWorker(self.repos, idx)
             self.install_worker.info_label.connect(self.set_information_label)
             self.install_worker.progressbar_show.connect(self.show_progress_bar)
             self.install_worker.start()
     elif self.tabWidget.currentIndex() == 1:
         # Tab "Macros".
         macro = self.macros[self.listMacros.currentRow()]
         if install_macro(macro, self.macro_repo_dir):
             self.labelDescription.setText(translate("AddonsInstaller", "Macro successfully installed. The macro is now available from the Macros dialog."))
         else:
             self.labelDescription.setText(translate("AddonsInstaller", "Unable to install"))
     self.update_status()
예제 #5
0
    def remove(self):

        "uninstalls a macro or workbench"

        if self.dialog.tabWidget.currentIndex() == 0:
            # Tab "Workbenches".
            idx = self.dialog.listWorkbenches.currentRow()
            basedir = FreeCAD.getUserAppDataDir()
            moddir = basedir + os.sep + "Mod"
            clonedir = moddir + os.sep + self.repos[idx][0]
            if os.path.exists(clonedir):
                shutil.rmtree(clonedir, onerror=self.remove_readonly)
                self.dialog.description.setText(
                    translate(
                        "AddonsInstaller",
                        "Addon successfully removed. Please restart FreeCAD"))
            else:
                self.dialog.description.setText(
                    translate("AddonsInstaller",
                              "Unable to remove this addon"))

        elif self.dialog.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.dialog.listMacros.currentRow()]
            if remove_macro(macro):
                self.dialog.description.setText(
                    translate('AddonsInstaller',
                              'Macro successfully removed.'))
            else:
                self.dialog.description.setText(
                    translate('AddonsInstaller',
                              'Macro could not be removed.'))
        self.update_status(soft=True)
        self.addon_removed = True  # A value to trigger the restart message on dialog close
예제 #6
0
 def run(self):
     self.progressbar_show.emit(True)
     self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
     if not self.macro.parsed and self.macro.on_git:
         self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from git'))
         self.macro.fill_details_from_file(self.macro.src_filename)
     if not self.macro.parsed and self.macro.on_wiki:
         self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from wiki'))
         mac = self.macro.name.replace(' ', '_')
         mac = mac.replace('&', '%26')
         mac = mac.replace('+', '%2B')
         url = 'https://www.freecadweb.org/wiki/Macro_' + mac
         self.macro.fill_details_from_wiki(url)
     if self.macro.is_installed():
         already_installed_msg = ('<strong>'
                 + translate("AddonsInstaller", "This addon is already installed.")
                 + '</strong><br>')
     else:
         already_installed_msg = ''
     message = (already_installed_msg
             + self.macro.desc
             + ' - <a href="'
             + self.macro.url
             + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">'
             + self.macro.url
             + '</span></a>')
     self.info_label.emit(message)
     self.progressbar_show.emit(False)
     self.stop = True
예제 #7
0
    def install(self,repos=None):

        """installs a workbench or macro"""

        if self.dialog.tabWidget.currentIndex() == 0:
            # Tab "Workbenches".
            idx = None
            if repos:
                idx = []
                for repo in repos:
                    for i,r in enumerate(self.repos):
                        if r[0] == repo:
                            idx.append(i)
            else:
                idx = self.dialog.listWorkbenches.currentRow()
            if idx != None:
                if hasattr(self,"install_worker") and self.install_worker:
                    if self.install_worker.isRunning():
                        return
                self.install_worker = InstallWorker(self.repos, idx)
                self.install_worker.info_label.connect(self.show_information)
                self.install_worker.progressbar_show.connect(self.show_progress_bar)
                self.install_worker.mark_recompute.connect(self.mark_recompute)
                self.install_worker.start()

        elif self.dialog.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.dialog.listMacros.currentRow()]
            if utils.install_macro(macro, self.macro_repo_dir):
                self.dialog.description.setText(translate("AddonsInstaller", "Macro successfully installed. The macro is now available from the Macros dialog."))
            else:
                self.dialog.description.setText(translate("AddonsInstaller", "Unable to install"))
예제 #8
0
 def run(self):
     self.progressbar_show.emit(True)
     self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
     if len(self.repos[self.idx]) == 4:
         desc = self.repos[self.idx][3]
     else:
         url = self.repos[self.idx][1]
         self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url))
         u = urlopen(url)
         p = u.read()
         if sys.version_info.major >= 3 and isinstance(p, bytes):
             p = p.decode("utf-8")
         u.close()
         desc = re.findall("<meta property=\"og:description\" content=\"(.*?)\"",p)
         if desc:
             desc = desc[0]
             if self.repos[self.idx][0] in OBSOLETE:
                 desc += " <b>This add-on is marked as obsolete</b> - This usually means it is no longer maintained, and some more advanced add-on in this list provides the same functionality."
         else:
             desc = "Unable to retrieve addon description"
         self.repos[self.idx].append(desc)
         self.addon_repos.emit(self.repos)
     if self.repos[self.idx][2] == 1:
         upd = False
         # checking for updates
         if not NOGIT:
             try:
                 import git
             except:
                 pass
             else:
                 repo = self.repos[self.idx]
                 clonedir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + repo[0]
                 if os.path.exists(clonedir):
                     if not os.path.exists(clonedir + os.sep + '.git'):
                         # Repair addon installed with raw download
                         bare_repo = git.Repo.clone_from(repo[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')
                     gitrepo = git.Git(clonedir)
                     gitrepo.fetch()
                     if "git pull" in gitrepo.status():
                         upd = True
         if upd:
             message = "<strong>" + translate("AddonsInstaller", "An update is available for this addon.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
         else:
             message = "<strong>" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
     else:
         message = desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
     self.info_label.emit( message )
     self.progressbar_show.emit(False)
     self.stop = True
예제 #9
0
    def run(self):

        self.progressbar_show.emit(True)
        self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
        if not self.macro.parsed and self.macro.on_git:
            self.info_label.emit(translate("AddonsInstaller", "Retrieving info from git"))
            self.macro.fill_details_from_file(self.macro.src_filename)
        if not self.macro.parsed and self.macro.on_wiki:
            self.info_label.emit(translate("AddonsInstaller", "Retrieving info from wiki"))
            mac = self.macro.name.replace(" ", "_")
            mac = mac.replace("&", "%26")
            mac = mac.replace("+", "%2B")
            url = "https://wiki.freecadweb.org/Macro_" + mac
            self.macro.fill_details_from_wiki(url)
        if self.macro.is_installed():
            already_installed_msg = ('<strong style=\"background: #00B629;\">'
                                     + translate("AddonsInstaller", "This macro is already installed.")
                                     + '</strong><br>')
        else:
            already_installed_msg = ""
        message = (already_installed_msg
                   + "<h1>"+self.macro.name+"</h1>"
                   + self.macro.desc
                   + "<br/><br/>Macro location: <a href=\""
                   + self.macro.url
                   + "\">"
                   + self.macro.url
                   + "</a>")
        self.info_label.emit(message)
        self.progressbar_show.emit(False)
        self.stop = True
예제 #10
0
 def download(self,giturl,clonedir):
     "downloads and unzip from github"
     import zipfile
     bakdir = None
     if os.path.exists(clonedir):
         bakdir = clonedir+".bak"
         if os.path.exists(bakdir):
             shutil.rmtree(bakdir)
         os.rename(clonedir,bakdir)
     os.makedirs(clonedir)
     zipurl = giturl+"/archive/master.zip"
     try:
         print("Downloading "+zipurl)
         u = urlopen(zipurl)
     except:
         return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
     zfile = _stringio()
     zfile.write(u.read())
     zfile = zipfile.ZipFile(zfile)
     master = zfile.namelist()[0] # github will put everything in a subfolder
     zfile.extractall(clonedir)
     u.close()
     zfile.close()
     for filename in os.listdir(clonedir+os.sep+master):
         shutil.move(clonedir+os.sep+master+os.sep+filename, clonedir+os.sep+filename)
     os.rmdir(clonedir+os.sep+master)
     if bakdir:
         shutil.rmtree(bakdir)
     return translate("AddonsInstaller", "Successfully installed") + " " + zipurl
예제 #11
0
 def reject(self):
     # ensure all threads are finished before closing
     oktoclose = True
     for worker in ["update_worker","check_worker","show_worker","showmacro_worker",
                    "macro_worker","install_worker"]:
         if hasattr(self,worker):
             thread = getattr(self,worker)
             if thread:
                 if not thread.isFinished():
                     oktoclose = False
     if oktoclose:
         if hasattr(self,"install_worker") or hasattr(self,"addon_removed"):
             m = QtGui.QMessageBox()
             m.setWindowTitle(translate("AddonsInstaller","Addon manager"))
             m.setText(translate("AddonsInstaller","You must restart FreeCAD for changes to take effect. Press Ok to restart FreeCAD now, or Cancel to restart later."))
             m.setIcon(m.Warning)
             m.setStandardButtons(m.Ok | m.Cancel)
             m.setDefaultButton(m.Cancel)
             ret = m.exec_()
             if ret == m.Ok:
                 shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly)
                 # restart FreeCAD after a delay to give time to close this dialog
                 QtCore.QTimer.singleShot(1000,restartFreeCAD)
         try:
             shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly)
         except:
             pass
         QtGui.QDialog.reject(self)
예제 #12
0
 def run(self):
     self.progressbar_show.emit(True)
     self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
     if not self.macro.parsed and self.macro.on_git:
         self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from git'))
         self.macro.fill_details_from_file(self.macro.src_filename)
     if not self.macro.parsed and self.macro.on_wiki:
         self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from wiki'))
         mac = self.macro.name.replace(' ', '_')
         mac = mac.replace('&', '%26')
         mac = mac.replace('+', '%2B')
         url = 'https://www.freecadweb.org/wiki/Macro_' + mac
         self.macro.fill_details_from_wiki(url)
     if self.macro.is_installed():
         already_installed_msg = ('<strong>'
                 + translate("AddonsInstaller", "This addon is already installed.")
                 + '</strong><br>')
     else:
         already_installed_msg = ''
     message = (already_installed_msg
             + self.macro.desc
             + ' - <a href="'
             + self.macro.url
             + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">'
             + self.macro.url
             + '</span></a>')
     self.info_label.emit(message)
     self.progressbar_show.emit(False)
     self.stop = True
예제 #13
0
 def run(self):
     "populates the list of addons"
     self.progressbar_show.emit(True)
     u = urlopen("https://github.com/FreeCAD/FreeCAD-addons")
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode("utf-8")
     u.close()
     p = p.replace("\n"," ")
     p = re.findall("octicon-file-submodule(.*?)message",p)
     basedir = FreeCAD.getUserAppDataDir()
     moddir = basedir + os.sep + "Mod"
     repos = []
     for l in p:
         #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
         name = re.findall("title=\"(.*?) @",l)[0]
         self.info_label.emit(name)
         #url = re.findall("title=\"(.*?) @",l)[0]
         url = "https://github.com/" + re.findall("href=\"\/(.*?)\/tree",l)[0]
         addondir = moddir + os.sep + name
         #print ("found:",name," at ",url)
         if not os.path.exists(addondir):
             state = 0
         else:
             state = 1
         repos.append([name,url,state])
     if not repos:
         self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
     else:
         repos = sorted(repos, key=lambda s: s[0].lower())
         for repo in repos:
             self.addon_repo.emit(repo)
         self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated."))
     self.progressbar_show.emit(False)
     self.stop = True
예제 #14
0
    def update_text_filter(self, text_filter: str) -> None:
        """filter name and description by the regex specified by text_filter"""

        if text_filter:
            if hasattr(self.item_filter, "setFilterRegularExpression"): # Added in Qt 5.12
                test_regex = QRegularExpression(text_filter)
            else:
                test_regex = QRegExp(text_filter)
            if test_regex.isValid():
                self.ui.labelFilterValidity.setToolTip(
                    translate("AddonsInstaller", "Filter is valid")
                )
                icon = QIcon.fromTheme("ok", QIcon(":/icons/edit_OK.svg"))
                self.ui.labelFilterValidity.setPixmap(icon.pixmap(16, 16))
            else:
                self.ui.labelFilterValidity.setToolTip(
                    translate("AddonsInstaller", "Filter regular expression is invalid")
                )
                icon = QIcon.fromTheme("cancel", QIcon(":/icons/edit_Cancel.svg"))
                self.ui.labelFilterValidity.setPixmap(icon.pixmap(16, 16))
            self.ui.labelFilterValidity.show()
        else:
            self.ui.labelFilterValidity.hide()
        if hasattr(self.item_filter, "setFilterRegularExpression"): # Added in Qt 5.12
            self.item_filter.setFilterRegularExpression(text_filter)
        else:
            self.item_filter.setFilterRegExp(text_filter)
예제 #15
0
    def retrieve_macros_from_git(self):

        """Retrieve macros from FreeCAD-macros.git

        Emits a signal for each macro in
        https://github.com/FreeCAD/FreeCAD-macros.git
        """

        try:
            import git
        except ImportError:
            self.info_label_signal.emit("GitPython not installed! Cannot retrieve macros from Git")
            FreeCAD.Console.PrintWarning(translate('AddonsInstaller', 'GitPython not installed! Cannot retrieve macros from git')+"\n")
            return

        self.info_label_signal.emit('Downloading list of macros from git...')
        try:
            git.Repo.clone_from('https://github.com/FreeCAD/FreeCAD-macros.git', self.repo_dir)
        except:
            FreeCAD.Console.PrintWarning(translate('AddonsInstaller', 'Something went wrong with the Git Macro Retrieval, possibly the Git executable is not in the path')+"\n")
        for dirpath, _, filenames in os.walk(self.repo_dir):
             if '.git' in dirpath:
                 continue
             for filename in filenames:
                 if filename.lower().endswith('.fcmacro'):
                    macro = Macro(filename[:-8])  # Remove ".FCMacro".
                    macro.on_git = True
                    macro.src_filename = os.path.join(dirpath, filename)
                    self.macros.append(macro)
예제 #16
0
    def remove(self, repo: AddonManagerRepo) -> None:
        """uninstalls a macro or workbench"""

        if (repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH
                or repo.repo_type == AddonManagerRepo.RepoType.PACKAGE):
            basedir = FreeCAD.getUserAppDataDir()
            moddir = basedir + os.sep + "Mod"
            clonedir = moddir + os.sep + repo.name
            if os.path.exists(clonedir):
                shutil.rmtree(clonedir, onerror=self.remove_readonly)
                self.item_model.update_item_status(
                    repo.name, AddonManagerRepo.UpdateStatus.NOT_INSTALLED)
                self.addon_removed = (
                    True  # A value to trigger the restart message on dialog close
                )
                self.packageDetails.show_repo(repo)
                self.restart_required = True
            else:
                self.dialog.textBrowserReadMe.setText(
                    translate(
                        "AddonsInstaller",
                        "Unable to remove this addon with the Addon Manager.",
                    ))

        elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
            macro = repo.macro
            if macro.remove():
                self.item_model.update_item_status(
                    repo.name, AddonManagerRepo.UpdateStatus.NOT_INSTALLED)
                self.packageDetails.show_repo(repo)
            else:
                self.dialog.textBrowserReadMe.setText(
                    translate("AddonsInstaller",
                              "Macro could not be removed."))
예제 #17
0
    def retrieve_macros_from_git(self):
        """Retrieve macros from FreeCAD-macros.git

        Emits a signal for each macro in
        https://github.com/FreeCAD/FreeCAD-macros.git
        """

        if not have_git:
            self.info_label_signal.emit("GitPython not installed! Cannot retrieve macros from Git")
            FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
                                                   "GitPython not installed! Cannot retrieve macros from git")+"\n")
            return

        self.info_label_signal.emit("Downloading list of macros from git...")
        try:
            git.Repo.clone_from("https://github.com/FreeCAD/FreeCAD-macros.git", self.repo_dir)
        except Exception:
            FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
                                                   "Something went wrong with the Git Macro Retrieval, "
                                                   "possibly the Git executable is not in the path") + "\n")
        for dirpath, _, filenames in os.walk(self.repo_dir):
            if ".git" in dirpath:
                continue
            for filename in filenames:
                if filename.lower().endswith(".fcmacro"):
                    macro = Macro(filename[:-8])  # Remove ".FCMacro".
                    macro.on_git = True
                    macro.src_filename = os.path.join(dirpath, filename)
                    self.macros.append(macro)
예제 #18
0
    def run(self):

        self.progressbar_show.emit(True)
        self.info_label.emit(
            translate("AddonsInstaller", "Retrieving description..."))
        if not self.macro.parsed and self.macro.on_git:
            self.info_label.emit(
                translate('AddonsInstaller', 'Retrieving info from git'))
            self.macro.fill_details_from_file(self.macro.src_filename)
        if not self.macro.parsed and self.macro.on_wiki:
            self.info_label.emit(
                translate('AddonsInstaller', 'Retrieving info from wiki'))
            mac = self.macro.name.replace(' ', '_')
            mac = mac.replace('&', '%26')
            mac = mac.replace('+', '%2B')
            url = 'https://www.freecadweb.org/wiki/Macro_' + mac
            self.macro.fill_details_from_wiki(url)
        if self.macro.is_installed():
            already_installed_msg = (
                '<strong style=\"background: #00B629;\">' + translate(
                    "AddonsInstaller", "This macro is already installed.") +
                '</strong><br>')
        else:
            already_installed_msg = ''
        message = (already_installed_msg + "<h1>" + self.macro.name + "</h1>" +
                   self.macro.desc + '<br/><br/>Macro location: <a href="' +
                   self.macro.url + '">' + self.macro.url + '</a>')
        self.info_label.emit(message)
        self.progressbar_show.emit(False)
        self.stop = True
예제 #19
0
    def run(self):

        "populates the list of addons"

        self.progressbar_show.emit(True)
        u = utils.urlopen("https://github.com/FreeCAD/FreeCAD-addons")
        if not u:
            self.progressbar_show.emit(False)
            self.done.emit()
            self.stop = True
            return
        p = u.read()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        u.close()
        p = p.replace("\n"," ")
        p = re.findall("octicon-file-submodule(.*?)message",p)
        basedir = FreeCAD.getUserAppDataDir()
        moddir = basedir + os.sep + "Mod"
        repos = []
        # querying official addons
        for l in p:
            #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
            name = re.findall("title=\"(.*?) @",l)[0]
            self.info_label.emit(name)
            #url = re.findall("title=\"(.*?) @",l)[0]
            url = utils.getRepoUrl(l)
            if url:
                addondir = moddir + os.sep + name
                #print ("found:",name," at ",url)
                if os.path.exists(addondir) and os.listdir(addondir):
                    # make sure the folder exists and it contains files!
                    state = 1
                else:
                    state = 0
                repos.append([name,url,state])
        # querying custom addons
        customaddons = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons").GetString("CustomRepositories","").split("\n")
        for url in customaddons:
            if url:
                name = url.split("/")[-1]
                if name.lower().endswith(".git"):
                    name = name[:-4]
                addondir = moddir + os.sep + name
                if not os.path.exists(addondir):
                    state = 0
                else:
                    state = 1
                repos.append([name,url,state])
        if not repos:
            self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
        else:
            repos = sorted(repos, key=lambda s: s[0].lower())
            for repo in repos:
                self.addon_repo.emit(repo)
            self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated."))
        self.progressbar_show.emit(False)
        self.done.emit()
        self.stop = True
예제 #20
0
    def reject(self) -> None:
        """called when the window has been closed"""

        # save window geometry for next use
        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        pref.SetInt("WindowWidth", self.dialog.width())
        pref.SetInt("WindowHeight", self.dialog.height())

        # ensure all threads are finished before closing
        oktoclose = True
        self.startup_sequence = []
        for worker in self.workers:
            if hasattr(self, worker):
                thread = getattr(self, worker)
                if thread:
                    if not thread.isFinished():
                        thread.requestInterruption()
                        oktoclose = False
        while not oktoclose:
            oktoclose = True
            for worker in self.workers:
                if hasattr(self, worker):
                    thread = getattr(self, worker)
                    if thread:
                        thread.wait(25)
                    if not thread.isFinished():
                        oktoclose = False
            QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)

        # Write the cache data
        for repo in self.item_model.repos:
            if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
                self.cache_macro(repo)
            else:
                self.cache_package(repo)
        self.write_package_cache()
        self.write_macro_cache()

        if self.restart_required:
            # display restart dialog
            m = QtWidgets.QMessageBox()
            m.setWindowTitle(translate("AddonsInstaller", "Addon manager"))
            m.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
            m.setText(
                translate(
                    "AddonsInstaller",
                    "You must restart FreeCAD for changes to take effect.",
                ))
            m.setIcon(m.Warning)
            m.setStandardButtons(m.Ok | m.Cancel)
            m.setDefaultButton(m.Cancel)
            okBtn = m.button(QtWidgets.QMessageBox.StandardButton.Ok)
            cancelBtn = m.button(QtWidgets.QMessageBox.StandardButton.Cancel)
            okBtn.setText(translate("AddonsInstaller", "Restart now"))
            cancelBtn.setText(translate("AddonsInstaller", "Restart later"))
            ret = m.exec_()
            if ret == m.Ok:
                # restart FreeCAD after a delay to give time to this dialog to close
                QtCore.QTimer.singleShot(1000, utils.restart_freecad)
예제 #21
0
    def checkDependencies(self,baseurl):

        "checks if the repo contains a metadata.txt and check its contents"

        import FreeCADGui
        ok = True
        message = ""
        depsurl = baseurl.replace("github.com","raw.githubusercontent.com")
        if not depsurl.endswith("/"):
            depsurl += "/"
        depsurl += "master/metadata.txt"
        try:
            mu = urlopen(depsurl)
        except:
            # no metadata.txt, we just continue without deps checking
            pass
        else:
            # metadata.txt found
            depsfile = mu.read()
            mu.close()

            # urllib2 gives us a bytelike object instead of a string. Have to consider that
            try:
                depsfile = depsfile.decode('utf-8')
            except AttributeError:
                pass

            deps = depsfile.split("\n")
            for l in deps:
                if l.startswith("workbenches="):
                    depswb = l.split("=")[1].split(",")
                    for wb in depswb:
                        if wb.strip():
                            if not wb.strip() in FreeCADGui.listWorkbenches().keys():
                                if not wb.strip()+"Workbench" in FreeCADGui.listWorkbenches().keys():
                                    ok = False
                                    message += translate("AddonsInstaller","Missing workbench") + ": " + wb + ", "
                elif l.startswith("pylibs="):
                    depspy = l.split("=")[1].split(",")
                    for pl in depspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                ok = False
                                message += translate("AddonsInstaller","Missing python module") +": " + pl + ", "
                elif l.startswith("optionalpylibs="):
                    opspy = l.split("=")[1].split(",")
                    for pl in opspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                message += translate("AddonsInstaller","Missing optional python module (doesn't prevent installing)") +": " + pl + ", "
        if message and (not ok):
            message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench") + ": <b>" + message + "</b>. "
            message += translate("AddonsInstaller","Please install the missing components first.")
        return ok, message
예제 #22
0
    def install(self, repo: AddonManagerRepo) -> None:
        """installs or updates a workbench, macro, or package"""

        if hasattr(self, "install_worker") and self.install_worker:
            if self.install_worker.isRunning():
                return

        if not repo:
            return

        if (repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH
                or repo.repo_type == AddonManagerRepo.RepoType.PACKAGE):
            self.show_progress_widgets()
            self.install_worker = InstallWorkbenchWorker(repo)
            self.install_worker.status_message.connect(self.show_information)
            self.current_progress_region = 1
            self.number_of_progress_regions = 1
            self.install_worker.progress_made.connect(self.update_progress_bar)
            self.install_worker.success.connect(self.on_package_installed)
            self.install_worker.failure.connect(self.on_installation_failed)
            self.install_worker.start()
        elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
            macro = repo.macro

            # To try to ensure atomicity, test the installation into a temp directory first,
            # and assume if that worked we have good odds of the real installation working
            failed = False
            errors = []
            with tempfile.TemporaryDirectory() as dir:
                temp_install_succeeded, error_list = macro.install(dir)
                if not temp_install_succeeded:
                    failed = True
                    errors = error_list

            if not failed:
                real_install_succeeded, errors = macro.install(
                    self.macro_repo_dir)
                if not real_install_succeeded:
                    failed = True
                else:
                    utils.update_macro_installation_details(repo)

            if not failed:
                message = translate(
                    "AddonsInstaller",
                    "Macro successfully installed. The macro is "
                    "now available from the Macros dialog.",
                )
                self.on_package_installed(repo, message)
            else:
                message = (translate("AddonsInstaller",
                                     "Installation of macro failed") + ":")
                for error in errors:
                    message += "\n  * "
                    message += error
                self.on_installation_failed(repo, message)
예제 #23
0
    def checkDependencies(self,baseurl):
        "checks if the repo contains a metadata.txt and check its contents"
        import FreeCADGui
        ok = True
        message = ""
        depsurl = baseurl.replace("github.com","raw.githubusercontent.com")
        if not depsurl.endswith("/"):
            depsurl += "/"
        depsurl += "master/metadata.txt"
        try:
            mu = urlopen(depsurl)
        except urllib2.HTTPError:
            # no metadata.txt, we just continue without deps checking
            pass
        else:
            # metadata.txt found
            depsfile = mu.read()
            mu.close()

            # urllib2 gives us a bytelike object instead of a string. Have to consider that
            try:
                depsfile = depsfile.decode('utf-8')
            except AttributeError:
                pass

            deps = depsfile.split("\n")
            for l in deps:
                if l.startswith("workbenches="):
                    depswb = l.split("=")[1].split(",")
                    for wb in depswb:
                        if wb.strip():
                            if not wb.strip() in FreeCADGui.listWorkbenches().keys():
                                if not wb.strip()+"Workbench" in FreeCADGui.listWorkbenches().keys():
                                    ok = False
                                    message += translate("AddonsInstaller","Missing workbench") + ": " + wb + ", "
                elif l.startswith("pylibs="):
                    depspy = l.split("=")[1].split(",")
                    for pl in depspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                ok = False
                                message += translate("AddonsInstaller","Missing python module") +": " + pl + ", "
                elif l.startswith("optionalpylibs="):
                    opspy = l.split("=")[1].split(",")
                    for pl in opspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                message += translate("AddonsInstaller","Missing optional python module (doesn't prevent installing)") +": " + pl + ", "
        if message and (not ok):
            message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench") + ": <b>" + message + "</b>. "
            message += translate("AddonsInstaller","Please install the missing components first.")
        return ok, message
예제 #24
0
 def enable_updates(self,num):
     
     """enables the update button"""
     
     if num:
         self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","Apply")+" "+str(num)+" "+translate("AddonsInstaller","update(s)"))
         self.dialog.buttonUpdateAll.setEnabled(True)
     else:
         self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","No update available"))
         self.dialog.buttonUpdateAll.setEnabled(False)
예제 #25
0
 def retranslateUi(self):
     self.setWindowTitle(translate("AddonsInstaller","Addon manager"))
     self.labelDescription.setText(translate("AddonsInstaller", "Downloading addon list..."))
     self.buttonExecute.setText(translate("AddonsInstaller", "Execute"))
     self.buttonExecute.setToolTip(translate("AddonsInstaller", "This button runs the selected macro (which must be installed first)"))
     self.buttonCheck.setToolTip(translate("AddonsInstaller", "Check for available updates"))
     self.buttonCancel.setText(translate("AddonsInstaller", "Close"))
     self.buttonInstall.setText(translate("AddonsInstaller", "Install / update"))
     self.buttonRemove.setText(translate("AddonsInstaller", "Remove"))
     self.tabWidget.setTabText(self.tabWidget.indexOf(self.listWorkbenches), translate("AddonsInstaller", "Workbenches"))
     self.tabWidget.setTabText(self.tabWidget.indexOf(self.listMacros), translate("AddonsInstaller", "Macros"))
예제 #26
0
    def reject(self):

        "called when the window has been closed"

        # save window geometry and splitter state for next use
        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        pref.SetInt("WindowWidth", self.dialog.width())
        pref.SetInt("WindowHeight", self.dialog.height())
        pref.SetInt("SplitterLeft", self.dialog.splitter.sizes()[0])
        pref.SetInt("SplitterRight", self.dialog.splitter.sizes()[1])

        # ensure all threads are finished before closing
        oktoclose = True
        for worker in [
                "update_worker", "check_worker", "show_worker",
                "showmacro_worker", "macro_worker", "install_worker"
        ]:
            if hasattr(self, worker):
                thread = getattr(self, worker)
                if thread:
                    if not thread.isFinished():
                        oktoclose = False

        # all threads have finished
        if oktoclose:
            if (hasattr(self, "install_worker") and self.install_worker) or (
                    hasattr(self, "addon_removed") and self.addon_removed):
                # display restart dialog
                from PySide import QtGui, QtCore
                m = QtGui.QMessageBox()
                m.setWindowTitle(translate("AddonsInstaller", "Addon manager"))
                m.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
                m.setText(
                    translate(
                        "AddonsInstaller",
                        "You must restart FreeCAD for changes to take effect. Press Ok to restart FreeCAD now, or Cancel to restart later."
                    ))
                m.setIcon(m.Warning)
                m.setStandardButtons(m.Ok | m.Cancel)
                m.setDefaultButton(m.Cancel)
                ret = m.exec_()
                if ret == m.Ok:
                    shutil.rmtree(self.macro_repo_dir,
                                  onerror=self.remove_readonly)
                    # restart FreeCAD after a delay to give time to this dialog to close
                    QtCore.QTimer.singleShot(1000, restartFreeCAD)
            try:
                shutil.rmtree(self.macro_repo_dir,
                              onerror=self.remove_readonly)
            except:
                pass

        return True
예제 #27
0
    def get_compact_update_string(self, repo: AddonManagerRepo) -> str:
        """Get a single-line string listing details about the installed version and date"""

        result = ""
        if repo.update_status == AddonManagerRepo.UpdateStatus.UNCHECKED:
            result = translate("AddonsInstaller", "Installed")
        elif repo.update_status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
            result = translate("AddonsInstaller", "Up-to-date")
        elif repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
            result = translate("AddonsInstaller", "Update available")
        elif repo.update_status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
            result = translate("AddonsInstaller", "Pending restart")
        return result
예제 #28
0
    def mark_recompute(self,addon):

        """marks an addon in the list as installed but needs recompute"""

        for i in range(self.dialog.listWorkbenches.count()):
            txt = self.dialog.listWorkbenches.item(i).text().strip()
            if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"):
                txt = txt[:-12]
            elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"):
                txt = txt[:-19]
            if txt == addon:
                from PySide import QtGui
                self.dialog.listWorkbenches.item(i).setText(txt+" ("+translate("AddonsInstaller","Restart required")+")")
                self.dialog.listWorkbenches.item(i).setIcon(QtGui.QIcon(":/icons/edit-undo.svg"))
예제 #29
0
    def update_status(self, soft=False):
        """Updates the list of workbenches/macros. If soft is true, items
        are not recreated (and therefore display text isn't triggered)"
        """

        moddir = FreeCAD.getUserAppDataDir() + os.sep + "Mod"
        if soft:
            for i in range(self.dialog.listWorkbenches.count()):
                txt = self.dialog.listWorkbenches.item(i).text().strip()
                ext = ""
                if txt.endswith(" ("+translate("AddonsInstaller", "Installed")+")"):
                    txt = txt[:-12]
                    ext = " ("+translate("AddonsInstaller", "Installed")+")"
                elif txt.endswith(" ("+translate("AddonsInstaller", "Update available")+")"):
                    txt = txt[:-19]
                    ext = " ("+translate("AddonsInstaller", "Update available")+")"
                elif txt.endswith(" ("+translate("AddonsInstaller", "Restart required")+")"):
                    txt = txt[:-19]
                    ext = " ("+translate("AddonsInstaller", "Restart required")+")"
                if os.path.exists(os.path.join(moddir, txt)):
                    self.dialog.listWorkbenches.item(i).setText(txt+ext)
                else:
                    self.dialog.listWorkbenches.item(i).setText(txt)
                    self.dialog.listWorkbenches.item(i).setIcon(self.get_icon(txt))
            for i in range(self.dialog.listMacros.count()):
                txt = self.dialog.listMacros.item(i).text().strip()
                if txt.endswith(" ("+translate("AddonsInstaller", "Installed")+")"):
                    txt = txt[:-12]
                elif txt.endswith(" ("+translate("AddonsInstaller", "Update available")+")"):
                    txt = txt[:-19]
                if os.path.exists(os.path.join(moddir, txt)):
                    self.dialog.listMacros.item(i).setText(txt+ext)
                else:
                    self.dialog.listMacros.item(i).setText(txt)
                    self.dialog.listMacros.item(i).setIcon(QtGui.QIcon(":/icons/document-python.svg"))
        else:
            self.dialog.listWorkbenches.clear()
            self.dialog.listMacros.clear()
            for wb in self.repos:
                if os.path.exists(os.path.join(moddir, wb[0])):
                    self.dialog.listWorkbenches.addItem(
                        QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"),
                                              str(wb[0]) + " (" +
                                              translate("AddonsInstaller", "Installed") + ")"))
                    wb[2] = 1
                else:
                    self.dialog.listWorkbenches.addItem(
                        QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"), str(wb[0])))
                    wb[2] = 0
            for macro in self.macros:
                if macro.is_installed():
                    self.dialog.listMacros.addItem(item)
                else:
                    self.dialog.listMacros.addItem(
                        QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"), macro.name))
예제 #30
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",
                        f"Failed to remove macro file '{dst_file}': it might not exist, or its permissions changed",
                    ) + "\n")
        return True
예제 #31
0
    def retrieve_macros_from_wiki(self):
        """Retrieve macros from the wiki

        Read the wiki and emit a signal for each found macro.
        Reads only the page https://www.freecadweb.org/wiki/Macros_recipes
        """

        self.info_label_signal.emit(
            "Downloading list of macros from the FreeCAD wiki...")
        self.progressbar_show.emit(True)
        u = utils.urlopen("https://www.freecadweb.org/wiki/Macros_recipes")
        if not u:
            FreeCAD.Console.PrintWarning(
                translate(
                    "AddonsInstaller",
                    "Appears to be an issue connecting to the Wiki, "
                    "therefore cannot retrieve Wiki macro list at this time") +
                "\n")
            return
        p = u.read()
        u.close()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        macros = re.findall('title="(Macro.*?)"', p)
        macros = [mac for mac in macros if ("translated" not in mac)]
        for mac in macros:
            macname = mac[6:]  # Remove "Macro ".
            macname = macname.replace("&amp;", "&")
            if (macname not in macros_blacklist) and ("recipes"
                                                      not in macname.lower()):
                macro = Macro(macname)
                macro.on_wiki = True
                self.macros.append(macro)
예제 #32
0
    def check_updates(self) -> None:
        "checks every installed addon for available updates"

        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        autocheck = pref.GetBool("AutoCheck", False)
        if not autocheck:
            FreeCAD.Console.PrintLog(
                "Addon Manager: Skipping update check because AutoCheck user preference is False\n"
            )
            self.do_next_startup_phase()
            return
        if not self.packages_with_updates:
            if hasattr(self, "check_worker"):
                thread = self.check_worker
                if thread:
                    if not thread.isFinished():
                        self.do_next_startup_phase()
                        return
            self.dialog.buttonUpdateAll.setText(
                translate("AddonsInstaller", "Checking for updates..."))
            self.check_worker = CheckWorkbenchesForUpdatesWorker(
                self.item_model.repos)
            self.check_worker.finished.connect(self.do_next_startup_phase)
            self.check_worker.progress_made.connect(self.update_progress_bar)
            self.check_worker.update_status.connect(self.status_updated)
            self.check_worker.start()
            self.enable_updates(len(self.packages_with_updates))
        else:
            self.do_next_startup_phase()
예제 #33
0
    def executemacro(self, repo: AddonManagerRepo) -> None:
        """executes a selected macro"""

        macro = repo.macro
        if not macro or not macro.code:
            return

        if macro.is_installed():
            macro_path = os.path.join(self.macro_repo_dir, macro.filename)
            FreeCADGui.open(str(macro_path))
            self.dialog.hide()
            FreeCADGui.SendMsgToActiveView("Run")
        else:
            with tempfile.TemporaryDirectory() as dir:
                temp_install_succeeded = macro.install(dir)
                if not temp_install_succeeded:
                    message = translate(
                        "AddonsInstaller",
                        "Execution of macro failed. See console for failure details.",
                    )
                    self.on_installation_failed(repo, message)
                    return
                else:
                    macro_path = os.path.join(dir, macro.filename)
                    FreeCADGui.open(str(macro_path))
                    self.dialog.hide()
                    FreeCADGui.SendMsgToActiveView("Run")
예제 #34
0
 def run(self):
     if NOGIT:
         self.stop = True
         return
     try:
         import git
     except:
         self.stop = True
         return
     self.progressbar_show.emit(True)
     basedir = FreeCAD.getUserAppDataDir()
     moddir = basedir + os.sep + "Mod"
     self.info_label.emit(translate("AddonsInstaller", "Checking for new versions..."))
     upds = []
     gitpython_warning = False
     for repo in self.repos:
         if repo[2] == 1: #installed
             self.info_label.emit(translate("AddonsInstaller","Checking repo")+" "+repo[0]+"...")
             clonedir = moddir + os.sep + repo[0]
             if os.path.exists(clonedir):
                 if not os.path.exists(clonedir + os.sep + '.git'):
                     # Repair addon installed with raw download
                     bare_repo = git.Repo.clone_from(repo[1], clonedir + os.sep + '.git', bare=True)
                     try:
                         with bare_repo.config_writer() as cw:
                             cw.set('core', 'bare', False)
                     except AttributeError:
                         if not gitpython_warning:
                             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
                             gitpython_warning = True
                         cw = bare_repo.config_writer()
                         cw.set('core', 'bare', False)
                         del cw
                     repo = git.Repo(clonedir)
                     repo.head.reset('--hard')
                 gitrepo = git.Git(clonedir)
                 gitrepo.fetch()
                 if "git pull" in gitrepo.status():
                     self.mark.emit(repo[0])
                     upds.append(repo[0])
     self.progressbar_show.emit(False)
     if upds:
         self.info_label.emit(str(len(upds))+" "+translate("AddonsInstaller", "update(s) available")+": "+",".join(upds)+". "+translate("AddonsInstaller","Press the update button again to update them all at once."))
         self.change_button.emit()
     else:
         self.info_label.emit(translate("AddonsInstaller","Everything is up to date"))
     self.stop = True
예제 #35
0
    def run(self):

        if NOGIT:
            self.stop = True
            return
        try:
            import git
        except:
            self.stop = True
            return
        basedir = FreeCAD.getUserAppDataDir()
        moddir = basedir + os.sep + "Mod"
        upds = []
        gitpython_warning = False
        for repo in self.repos:
            if repo[2] == 1:  #installed
                #print("Checking for updates for",repo[0])
                clonedir = moddir + os.sep + repo[0]
                if os.path.exists(clonedir):
                    if not os.path.exists(clonedir + os.sep + '.git'):
                        # Repair addon installed with raw download
                        bare_repo = git.Repo.clone_from(repo[1],
                                                        clonedir + os.sep +
                                                        '.git',
                                                        bare=True)
                        try:
                            with bare_repo.config_writer() as cw:
                                cw.set('core', 'bare', False)
                        except AttributeError:
                            if not gitpython_warning:
                                FreeCAD.Console.PrintWarning(
                                    translate(
                                        "AddonsInstaller",
                                        "Outdated GitPython detected, consider upgrading with pip."
                                    ) + "\n")
                                gitpython_warning = True
                            cw = bare_repo.config_writer()
                            cw.set('core', 'bare', False)
                            del cw
                        repo = git.Repo(clonedir)
                        repo.head.reset('--hard')
                    gitrepo = git.Git(clonedir)
                    try:
                        gitrepo.fetch()
                    except:
                        print(
                            "AddonManager: Unable to fetch git updates for repo",
                            repo[0])
                    else:
                        if "git pull" in gitrepo.status():
                            self.mark.emit(repo[0])
                            upds.append(repo[0])
                self.repos[self.repos.index(
                    repo
                )][2] = 2  # mark as already installed AND already checked for updates
        self.addon_repos.emit(self.repos)
        self.enable.emit(len(upds))
        self.stop = True
예제 #36
0
 def run(self):
     """Populates the list of macros"""
     self.retrieve_macros_from_git()
     self.retrieve_macros_from_wiki()
     [self.add_macro_signal.emit(m) for m in sorted(self.macros, key=lambda m: m.name.lower())]
     if self.macros:
         self.info_label_signal.emit(translate('AddonsInstaller', 'List of macros successfully retrieved.'))
     self.progressbar_show.emit(False)
     self.stop = True
예제 #37
0
 def remove(self):
     if self.tabWidget.currentIndex() == 0:
         # Tab "Workbenches".
         idx = self.listWorkbenches.currentRow()
         basedir = FreeCAD.getUserAppDataDir()
         moddir = basedir + os.sep + "Mod"
         clonedir = moddir + os.sep + self.repos[idx][0]
         if os.path.exists(clonedir):
             shutil.rmtree(clonedir, onerror=self.remove_readonly)
             self.labelDescription.setText(translate("AddonsInstaller", "Addon successfully removed. Please restart FreeCAD"))
         else:
             self.labelDescription.setText(translate("AddonsInstaller", "Unable to remove this addon"))
     elif self.tabWidget.currentIndex() == 1:
         # Tab "Macros".
         macro = self.macros[self.listMacros.currentRow()]
         if remove_macro(macro):
             self.labelDescription.setText(translate('AddonsInstaller', 'Macro successfully removed.'))
         else:
             self.labelDescription.setText(translate('AddonsInstaller', 'Macro could not be removed.'))
     self.update_status()
예제 #38
0
def launchAddonMgr():
    # first use dialog
    readWarning = FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').GetBool('readWarning',False)
    if not readWarning:
        if QtGui.QMessageBox.warning(None,"FreeCAD",translate("AddonsInstaller", "The addons that can be installed here are not officially part of FreeCAD, and are not reviewed by the FreeCAD team. Make sure you know what you are installing!"), QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok) != QtGui.QMessageBox.StandardButton.Cancel:
            FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').SetBool('readWarning',True)
            readWarning = True

    if readWarning:
        dialog = AddonsInstaller()
        dialog.exec_()
예제 #39
0
 def reject(self):
     # ensure all threads are finished before closing
     oktoclose = True
     for worker in ["update_worker","check_worker","show_worker","showmacro_worker",
                    "macro_worker","install_worker"]:
         if hasattr(self,worker):
             thread = getattr(self,worker)
             if thread:
                 if not thread.isFinished():
                     oktoclose = False
     if oktoclose:
         if hasattr(self,"install_worker"):
             QtGui.QMessageBox.information(self, translate("AddonsInstaller","Addon manager"), translate("AddonsInstaller","Please restart FreeCAD for changes to take effect."))
         shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly)
         QtGui.QDialog.reject(self)
예제 #40
0
    def retrieve_macros_from_git(self):
        """Retrieve macros from FreeCAD-macros.git

        Emits a signal for each macro in
        https://github.com/FreeCAD/FreeCAD-macros.git
        """
        try:
            import git
        except ImportError:
            self.info_label_signal.emit("GitPython not installed! Cannot retrieve macros from git")
            FreeCAD.Console.PrintWarning(translate('AddonInstaller', 'GitPython not installed! Cannot retrieve macros from git')+"\n")
            return

        self.info_label_signal.emit('Downloading list of macros for git...')
        git.Repo.clone_from('https://github.com/FreeCAD/FreeCAD-macros.git', self.repo_dir)
        for dirpath, _, filenames in os.walk(self.repo_dir):
             if '.git' in dirpath:
                 continue
             for filename in filenames:
                 if filename.lower().endswith('.fcmacro'):
                    macro = Macro(filename[:-8])  # Remove ".FCMacro".
                    macro.on_git = True
                    macro.src_filename = os.path.join(dirpath, filename)
                    self.macros.append(macro)
예제 #41
0
    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
예제 #42
0
 def change_update_button(self):
     self.buttonCheck.setText(translate("AddonsInstaller", "Update all"))
     self.buttonCheck.setToolTip(translate("AddonsInstaller", "Apply all available updates"))
예제 #43
0
 def fill_details_from_wiki(self, url):
     code = ""
     try:
         u = urlopen(url)
     except urllib2.HTTPError:
         return
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode('utf-8')
     u.close()
     # check if the macro page has its code hosted elsewhere, download if needed
     if "rawcodeurl" in p:
         rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">",p)
         if rawcodeurl:
             rawcodeurl = rawcodeurl[0]
             try:
                 u2 = urlopen(rawcodeurl)
             except urllib2.HTTPError:
                 return
             # code = u2.read()
             # github is slow to respond... We need to use this trick below
             response = ""
             block = 8192
             #expected = int(u2.headers['content-length'])
             while 1:
                 #print("expected:",expected,"got:",len(response))
                 data = u2.read(block)
                 if not data:
                     break
                 if sys.version_info.major >= 3 and isinstance(data, bytes):
                     data = data.decode('utf-8')
                 response += data
             if response:
                 code = response
             u2.close()
     if not code:
         code = re.findall('<pre>(.*?)<\/pre>', p.replace('\n', '--endl--'))
         if code:
             # code = code[0]
             # take the biggest code block
             code = sorted(code, key=len)[-1]
             code = code.replace('--endl--', '\n')
         else:
             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to fetch the code of this macro."))
         # Clean HTML escape codes.
         try:
             from HTMLParser import HTMLParser
         except ImportError:
             from html.parser import HTMLParser
         if sys.version_info.major < 3:
             code = code.decode('utf8')
         try:
             code = HTMLParser().unescape(code)
             code = code.replace(b'\xc2\xa0'.decode("utf-8"), ' ')
         except:
             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to clean macro code: ") + code + '\n')
         if sys.version_info.major < 3:
             code = code.encode('utf8')
     desc = re.findall("<td class=\"ctEven left macro-description\">(.*?)<\/td>", p.replace('\n', ' '))
     if desc:
         desc = desc[0]
     else:
         FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to retrieve a description for this macro."))
         desc = "No description available"
     self.desc = desc
     self.url = url
     self.code = code
     self.parsed = True