[AddonManager] Add support for __Files__
Read the metadata __Files__ for macros from git and install or remove files listed there. __Files__ must be a comma-separated list of files to be copied alongside the macro. Their path must be relative to the macro (*.FCMacro file) and not to othe root of the repository. Each file can be prefixed with a subdirectory.
This commit is contained in:
committed by
Yorik van Havre
parent
bac786a8ea
commit
37b738f86f
@@ -44,7 +44,8 @@ import sys
|
||||
import tempfile
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCAD,FreeCADGui
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
if sys.version_info.major < 3:
|
||||
import urllib2
|
||||
else:
|
||||
@@ -105,6 +106,92 @@ def update_macro_details(old_macro, new_macro):
|
||||
setattr(old_macro, attr, getattr(new_macro, attr))
|
||||
|
||||
|
||||
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()
|
||||
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 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()
|
||||
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
|
||||
|
||||
|
||||
def remove_directory_if_empty(dir):
|
||||
"""Remove the directory if it is empty
|
||||
|
||||
Directory FreeCAD.getUserMacroDir() will not be removed even if empty.
|
||||
"""
|
||||
if dir == FreeCAD.getUserMacroDir():
|
||||
return
|
||||
if not os.listdir(dir):
|
||||
os.rmdir(dir)
|
||||
|
||||
|
||||
class AddonsInstaller(QtGui.QDialog):
|
||||
|
||||
def __init__(self):
|
||||
@@ -331,22 +418,11 @@ class AddonsInstaller(QtGui.QDialog):
|
||||
self.install_worker.start()
|
||||
elif self.tabWidget.currentIndex() == 1:
|
||||
# Tab "Macros".
|
||||
macro_dir = FreeCAD.getUserMacroDir()
|
||||
if not os.path.isdir(macro_dir):
|
||||
os.makedirs(macro_dir)
|
||||
macro = self.macros[self.listMacros.currentRow()]
|
||||
if not macro.code:
|
||||
self.labelDescription.setText(translate("AddonsInstaller", "Unable to install"))
|
||||
return
|
||||
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'
|
||||
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:
|
||||
mode = 'w'
|
||||
with open(macro_path, mode) as macrofile:
|
||||
macrofile.write(macro.code)
|
||||
self.labelDescription.setText(translate("AddonsInstaller", "Macro successfully installed. The macro is now available from the Macros dialog."))
|
||||
self.labelDescription.setText(translate("AddonsInstaller", "Unable to install"))
|
||||
self.update_status()
|
||||
|
||||
def show_progress_bar(self, state):
|
||||
@@ -380,14 +456,13 @@ class AddonsInstaller(QtGui.QDialog):
|
||||
if not macro.is_installed():
|
||||
# Macro not installed, nothing to do.
|
||||
return
|
||||
macro_path = os.path.join(get_macro_dir(), macro.filename)
|
||||
macro_path = os.path.join(FreeCAD.getUserMacroDir(), macro.filename)
|
||||
if os.path.exists(macro_path):
|
||||
macro_path = macro_path.replace("\\","/")
|
||||
# FreeCAD.Console.PrintMessage(str(macro_path) + "\n")
|
||||
|
||||
FreeCADGui.open(str(macro_path))
|
||||
self.hide()
|
||||
Gui.SendMsgToActiveView("Run")
|
||||
FreeCADGui.SendMsgToActiveView("Run")
|
||||
else:
|
||||
self.buttonExecute.setEnabled(False)
|
||||
|
||||
@@ -411,17 +486,10 @@ class AddonsInstaller(QtGui.QDialog):
|
||||
elif 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(), macro.filename)
|
||||
macro_path_with_macro_prefix = os.path.join(FreeCAD.getUserMacroDir(), 'Macro_' + macro.filename)
|
||||
if os.path.exists(macro_path):
|
||||
os.remove(macro_path)
|
||||
self.labelDescription.setText(translate("AddonsInstaller", "Macro successfully removed."))
|
||||
elif os.path.exists(macro_path_with_macro_prefix):
|
||||
os.remove(macro_path_with_macro_prefix)
|
||||
self.labelDescription.setText(translate("AddonsInstaller", "Macro successfully removed."))
|
||||
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()
|
||||
|
||||
def update_status(self):
|
||||
@@ -847,7 +915,7 @@ class InstallWorker(QtCore.QThread):
|
||||
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 = get_macro_dir()
|
||||
macro_dir = FreeCAD.getUserMacroDir()
|
||||
if not os.path.exists(macro_dir):
|
||||
os.makedirs(macro_dir)
|
||||
for f in os.listdir(clonedir):
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 Gaël Écorchard <galou_breizh@yahoo.fr> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import os
|
||||
import re
|
||||
@@ -21,6 +42,7 @@ class Macro(object):
|
||||
self.url = ''
|
||||
self.version = ''
|
||||
self.src_filename = ''
|
||||
self.other_files = []
|
||||
self.parsed = False
|
||||
|
||||
def __eq__(self, other):
|
||||
@@ -40,10 +62,13 @@ class Macro(object):
|
||||
|
||||
def fill_details_from_file(self, filename):
|
||||
with open(filename) as f:
|
||||
number_of_required_fields = 3 # Fields __Comment__, __Web__, __Version__.
|
||||
# Number of parsed fields of metadata. For now, __Comment__,
|
||||
# __Web__, __Version__, __Files__.
|
||||
number_of_required_fields = 4
|
||||
re_desc = re.compile(r"^__Comment__\s*=\s*(['\"])(.*)\1")
|
||||
re_url = re.compile(r"^__Web__\s*=\s*(['\"])(.*)\1")
|
||||
re_version = re.compile(r"^__Version__\s*=\s*(['\"])(.*)\1")
|
||||
re_files = re.compile(r"^__Files__\s*=\s*(['\"])(.*)\1")
|
||||
for l in f.readlines():
|
||||
match = re.match(re_desc, l)
|
||||
if match:
|
||||
@@ -57,6 +82,10 @@ class Macro(object):
|
||||
if match:
|
||||
self.version = match.group(2)
|
||||
number_of_required_fields -= 1
|
||||
match = re.match(re_files, l)
|
||||
if match:
|
||||
self.other_files = match.group(2).split(',')
|
||||
number_of_required_fields -= 1
|
||||
if number_of_required_fields <= 0:
|
||||
break
|
||||
f.seek(0)
|
||||
|
||||
Reference in New Issue
Block a user