FreeCAD

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

24
"""Classes to encapsulate the Addon Manager's interaction with FreeCAD, and to provide
25
replacements when the Addon Manager is not run from within FreeCAD (e.g. during unit
26
testing).
27

28
Usage:
29
from addonmanager_freecad_interface import Console, DataPaths, Preferences
30
"""
31

32
import json
33
import logging
34
import os
35
import tempfile
36

37
# pylint: disable=too-few-public-methods
38

39
try:
40
    import FreeCAD
41

42
    Console = FreeCAD.Console
43
    ParamGet = FreeCAD.ParamGet
44
    Version = FreeCAD.Version
45
    getUserAppDataDir = FreeCAD.getUserAppDataDir
46
    getUserMacroDir = FreeCAD.getUserMacroDir
47
    getUserCachePath = FreeCAD.getUserCachePath
48
    translate = FreeCAD.Qt.translate
49

50
    if FreeCAD.GuiUp:
51
        import FreeCADGui
52
    else:
53
        FreeCADGui = None
54

55
except ImportError:
56
    FreeCAD = None
57
    FreeCADGui = None
58
    getUserAppDataDir = None
59
    getUserCachePath = None
60
    getUserMacroDir = None
61

62
    def translate(_context: str, string: str, _desc: str = "") -> str:
63
        return string
64

65
    def Version():
66
        return 0, 21, 0, "dev"
67

68
    class ConsoleReplacement:
69
        """If FreeCAD's Console is not available, create a replacement by redirecting FreeCAD
70
        log calls to Python's built-in logging facility."""
71

72
        @staticmethod
73
        def PrintLog(arg: str) -> None:
74
            logging.log(logging.DEBUG, arg)
75

76
        @staticmethod
77
        def PrintMessage(arg: str) -> None:
78
            logging.info(arg)
79

80
        @staticmethod
81
        def PrintWarning(arg: str) -> None:
82
            logging.warning(arg)
83

84
        @staticmethod
85
        def PrintError(arg: str) -> None:
86
            logging.error(arg)
87

88
    Console = ConsoleReplacement()
89

90
    class ParametersReplacement:
91
        """Proxy for FreeCAD's Parameters when not running within FreeCAD. NOT
92
        serialized, only exists for the duration of the program's execution. Only
93
        provides the functions used by the Addon Manager, this class is not intended
94
        to be a complete replacement for FreeCAD's preferences system."""
95

96
        parameters = {}
97

98
        def GetBool(self, name: str, default: bool) -> bool:
99
            return self._Get(name, default)
100

101
        def GetInt(self, name: str, default: int) -> int:
102
            return self._Get(name, default)
103

104
        def GetFloat(self, name: str, default: float) -> float:
105
            return self._Get(name, default)
106

107
        def GetString(self, name: str, default: str) -> str:
108
            return self._Get(name, default)
109

110
        def _Get(self, name, default):
111
            return self.parameters[name] if name in self.parameters else default
112

113
        def SetBool(self, name: str, value: bool) -> None:
114
            self.parameters[name] = value
115

116
        def SetInt(self, name: str, value: int) -> None:
117
            self.parameters[name] = value
118

119
        def SetFloat(self, name: str, value: float) -> None:
120
            self.parameters[name] = value
121

122
        def SetString(self, name: str, value: str) -> None:
123
            self.parameters[name] = value
124

125
        def RemBool(self, name: str) -> None:
126
            self.parameters.pop(name)
127

128
        def RemInt(self, name: str) -> None:
129
            self.parameters.pop(name)
130

131
        def RemFloat(self, name: str) -> None:
132
            self.parameters.pop(name)
133

134
        def RemString(self, name: str) -> None:
135
            self.parameters.pop(name)
136

137
    def ParamGet(_: str):
138
        return ParametersReplacement()
139

140

141
class DataPaths:
142
    """Provide access to various data storage paths. If not running within FreeCAD,
143
    all paths are temp directories. If not run within FreeCAD, all directories are
144
    deleted when the last reference to this class is deleted."""
145

146
    mod_dir = None
147
    macro_dir = None
148
    cache_dir = None
149
    home_dir = None
150

151
    reference_count = 0
152

153
    def __init__(self):
154
        if FreeCAD:
155
            if self.mod_dir is None:
156
                self.mod_dir = os.path.join(getUserAppDataDir(), "Mod")
157
            if self.cache_dir is None:
158
                self.cache_dir = getUserCachePath()
159
            if self.macro_dir is None:
160
                self.macro_dir = getUserMacroDir(True)
161
            if self.home_dir is None:
162
                self.home_dir = FreeCAD.getHomePath()
163
        else:
164
            self.reference_count += 1
165
            if self.mod_dir is None:
166
                self.mod_dir = tempfile.mkdtemp()
167
            if self.cache_dir is None:
168
                self.cache_dir = tempfile.mkdtemp()
169
            if self.macro_dir is None:
170
                self.macro_dir = tempfile.mkdtemp()
171
            if self.home_dir is None:
172
                self.home_dir = os.path.join(os.path.dirname(__file__), "..", "..")
173

174
    def __del__(self):
175
        self.reference_count -= 1
176
        if not FreeCAD and self.reference_count <= 0:
177
            os.rmdir(self.mod_dir)
178
            os.rmdir(self.cache_dir)
179
            os.rmdir(self.macro_dir)
180
            self.mod_dir = None
181
            self.cache_dir = None
182
            self.macro_dir = None
183

184

185
class Preferences:
186
    """Wrap access to all user preferences. If run within FreeCAD, user preferences are
187
    persistent, otherwise they only exist per-run. All preferences are controlled by a
188
    central JSON file defining their defaults."""
189

190
    preferences_defaults = {}
191

192
    def __init__(self, defaults_data=None):
193
        """Set up the preferences, initializing the class statics if necessary. If
194
        defaults_data is provided it is used as the preferences defaults. If it is not
195
        provided, then the defaults are read in from the standard defaults file,
196
        addonmanager_preferences_defaults.json, located in the same directory as this
197
        Python file."""
198
        if not self.preferences_defaults:
199
            if defaults_data:
200
                self.preferences_defaults = defaults_data
201
            else:
202
                self._load_preferences_defaults()
203
        self.prefs = ParamGet("User parameter:BaseApp/Preferences/Addons")
204

205
    def get(self, name: str):
206
        """Get the preference value for the given key"""
207
        if name not in self.preferences_defaults:
208
            raise RuntimeError(
209
                f"Unrecognized preference {name} -- did you add "
210
                + "it to addonmanager_preferences_defaults.json?"
211
            )
212
        if isinstance(self.preferences_defaults[name], bool):
213
            return self.prefs.GetBool(name, self.preferences_defaults[name])
214
        if isinstance(self.preferences_defaults[name], int):
215
            return self.prefs.GetInt(name, self.preferences_defaults[name])
216
        if isinstance(self.preferences_defaults[name], float):
217
            return self.prefs.GetFloat(name, self.preferences_defaults[name])
218
        if isinstance(self.preferences_defaults[name], str):
219
            return self.prefs.GetString(name, self.preferences_defaults[name])
220
        # We don't directly support any other types from the JSON file (e.g. arrays)
221
        type_name = type(self.preferences_defaults[name])
222
        raise RuntimeError(f"Unrecognized type for {name}: {type_name}")
223

224
    def set(self, name: str, value):
225
        """Set the preference value for the given key. Must exist (e.g. must be in the
226
        addonmanager_preferences_defaults.json file)."""
227
        if name not in self.preferences_defaults:
228
            raise RuntimeError(
229
                f"Unrecognized preference {name} -- did you add "
230
                + "it to addonmanager_preferences_defaults.json?"
231
            )
232
        if isinstance(self.preferences_defaults[name], bool):
233
            self.prefs.SetBool(name, value)
234
        elif isinstance(self.preferences_defaults[name], int):
235
            self.prefs.SetInt(name, value)
236
        elif isinstance(self.preferences_defaults[name], float):
237
            self.prefs.SetFloat(name, value)
238
        elif isinstance(self.preferences_defaults[name], str):
239
            self.prefs.SetString(name, value)
240
        else:
241
            # We don't directly support any other types from the JSON file (e.g. arrays)
242
            type_name = type(self.preferences_defaults[name])
243
            raise RuntimeError(f"Unrecognized type for {name}: {type_name}")
244

245
    def rem(self, name: str):
246
        """Remove the preference. Must have an entry in the
247
        addonmanager_preferences_defaults.json file."""
248
        if name not in self.preferences_defaults:
249
            raise RuntimeError(
250
                f"Unrecognized preference {name} -- did you add "
251
                + "it to addonmanager_preferences_defaults.json?"
252
            )
253
        if isinstance(self.preferences_defaults[name], bool):
254
            return self.prefs.RemBool(name)
255
        if isinstance(self.preferences_defaults[name], int):
256
            return self.prefs.RemInt(name)
257
        if isinstance(self.preferences_defaults[name], float):
258
            return self.prefs.RemFloat(name)
259
        if isinstance(self.preferences_defaults[name], str):
260
            return self.prefs.RemString(name)
261
        # We don't directly support any other types from the JSON file (e.g. arrays)
262
        type_name = type(self.preferences_defaults[name])
263
        raise RuntimeError(f"Unrecognized type for {name}: {type_name}")
264

265
    @classmethod
266
    def _load_preferences_defaults(cls, filename=None):
267
        """Loads the preferences defaults JSON file from either a specified file, or
268
        from the standard addonmanager_preferences_defaults.json file."""
269

270
        if filename is None:
271
            json_file = os.path.join(
272
                os.path.dirname(__file__), "addonmanager_preferences_defaults.json"
273
            )
274
        else:
275
            json_file = filename
276
        with open(json_file, "r", encoding="utf-8") as f:
277
            file_contents = f.read()
278
        cls.preferences_defaults = json.loads(file_contents)
279

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

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

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

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