Skip to content

Commit d1145c1

Browse files
authored
Merge pull request #153 from hsorby/profiles
Add plugin manager profiles
2 parents f233a7f + f488f9b commit d1145c1

20 files changed

Lines changed: 492 additions & 16708 deletions

src/mapclient/application.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@
5555
logger = logging.getLogger('mapclient.application')
5656

5757

58+
def get_app_path():
59+
if getattr(sys, 'frozen', False):
60+
# If the application is frozen (e.g., by PyInstaller)
61+
return os.path.dirname(sys.executable)
62+
else:
63+
# If running in a normal Python environment
64+
return os.path.dirname(os.path.abspath(__file__))
65+
66+
5867
def initialise_logger(log_path):
5968
"""
6069
Initialise logger settings and information formatting
@@ -374,7 +383,7 @@ def _user_specified_environment_main(base_dir, directories):
374383
if os.path.isdir(d) and d not in plugin_directories:
375384
plugin_directories.append(d)
376385

377-
pm.setDirectories(plugin_directories)
386+
pm.set_directories(plugin_directories)
378387
model.writeSettings()
379388

380389
logger.info(f"Set environment variable '{APPLICATION_ENVIRONMENT_CONFIG_DIR_VARIABLE}' to '{config_dir}' to use application with these settings.")

src/mapclient/core/managers/pluginmanager.py

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
44
@author: hsorby
55
"""
6-
import io
76
import json
87
import os
98
import sys
@@ -16,37 +15,38 @@
1615
import types
1716
import importlib
1817

19-
from contextlib import redirect_stdout
20-
21-
22-
from mapclient.core.utils import which, FileTypeObject, grep, is_frozen, determine_step_name, determine_step_class_name
18+
from mapclient.application import get_app_path
19+
from mapclient.core.utils import which, FileTypeObject, grep, is_frozen, determine_step_name, determine_step_class_name, \
20+
stable_hash
2321
from mapclient.settings.definitions import VIRTUAL_ENV_PATH, \
2422
PLUGINS_PACKAGE_NAME, PLUGINS_PTH
2523
from mapclient.core.checks import getPipExecutable
2624

2725
from importlib import import_module
2826

29-
from mapclient.settings.general import get_virtualenv_site_packages_directory
27+
from mapclient.settings.general import get_virtualenv_site_packages_directory, get_settings
3028

3129
logger = logging.getLogger(__name__)
3230

31+
CONST_DEFAULT_PROFILE = 'Default'
32+
3333

3434
def getVirtualEnvCandidates():
3535
"""Return a list of strings which contains possible names
3636
of the virtualenv program for this environment.
3737
"""
38-
if sys.version_info < (3, 0):
39-
virtualenv_candidates = [which('virtualenv'), which('virtualenv2')]
40-
else:
41-
virtualenv_candidates = [which('virtualenv'), which('virtualenv3')]
38+
return [which('virtualenv'), which('virtualenv3')]
39+
4240

43-
return virtualenv_candidates
41+
def _get_app_profile_key():
42+
return f'current_profile_{stable_hash(get_app_path())}'
4443

4544

4645
class PluginManager:
4746

4847
def __init__(self):
49-
self._directories = []
48+
self._current_profile = CONST_DEFAULT_PROFILE
49+
self._profile_directories = {}
5050
self._virtualenv_enabled = True
5151
self._virtualenv_dir = None
5252
self._virtualenv_setup_attempted = False
@@ -74,7 +74,13 @@ def getVirtualEnvDirectory(self):
7474
return self._virtualenv_dir
7575

7676
def directories(self):
77-
return self._directories
77+
return self._profile_directories.get(self._current_profile, [])
78+
79+
def current_profile(self):
80+
return self._current_profile
81+
82+
def set_current_profile(self, profile_name):
83+
self._current_profile = profile_name
7884

7985
def setReloadPlugins(self, state=True):
8086
self._reload_plugins = state
@@ -105,14 +111,15 @@ def list(self):
105111
def setOptions(self, options):
106112
self._virtualenv_dir = options[VIRTUAL_ENV_PATH]
107113

108-
def setDirectories(self, directories):
114+
def set_directories(self, directories):
109115
"""
110116
Set the list of directories to be searched for
111117
plugins. Returns true if the directories listing
112118
was updated and false otherwise.
113119
"""
114-
if self._directories != directories:
115-
self._directories = directories
120+
current_directories = self._profile_directories.get(self._current_profile, [])
121+
if current_directories != directories:
122+
self._profile_directories[self._current_profile] = directories
116123
self._reload_plugins = True
117124

118125
def virtualenvSetupAttempted(self):
@@ -165,11 +172,6 @@ def setupVirtualEnv(self):
165172
def getPluginDatabase(self):
166173
return self._plugin_database
167174

168-
def allDirectories(self):
169-
plugin_dirs = self._directories[:]
170-
171-
return plugin_dirs
172-
173175
def _addPluginDir(self, directory):
174176
added = False
175177
if isMapClientPluginsDir(directory):
@@ -223,7 +225,7 @@ def load(self):
223225

224226
len_package_modules_prior = len(sys.modules[PLUGINS_PACKAGE_NAME].__path__) if PLUGINS_PACKAGE_NAME in sys.modules else 0
225227
new_plugin_directories = []
226-
for directory in self.allDirectories():
228+
for directory in self.directories():
227229
if self._addPluginDir(directory):
228230
new_plugin_directories.append(directory)
229231
else:
@@ -328,17 +330,38 @@ def getPluginErrors(self):
328330
return {'ImportError': self._import_errors, 'TypeError': self._type_errors, 'SyntaxError': self._syntax_errors, 'TabError': self._tab_errors,
329331
'directories': self._plugin_error_directories}
330332

333+
def set_profile_directories(self, profile_directories):
334+
"""
335+
Set the directories for the given profile name.
336+
"""
337+
self._profile_directories = profile_directories
338+
339+
def profile_directories(self):
340+
"""
341+
Get the profile directories information.
342+
"""
343+
return self._profile_directories
344+
331345
def readSettings(self, settings):
332-
self._directories = []
346+
self._profile_directories = {}
333347
settings.beginGroup('Plugins')
334348
self._doNotShowPluginErrors = settings.value('donot_show_plugin_errors', 'true') == 'true'
335349
self._virtualenv_setup_attempted = settings.value('virtualenv_setup_attempted', 'false') == 'true'
336-
directory_count = settings.beginReadArray('directories')
337-
for i in range(directory_count):
350+
self._current_profile = settings.value(_get_app_profile_key(), CONST_DEFAULT_PROFILE)
351+
profiles_count = settings.beginReadArray('profiles')
352+
for i in range(profiles_count):
338353
settings.setArrayIndex(i)
339-
self._directories.append(settings.value('directory'))
354+
profile_name = settings.value('name')
355+
directory_count = settings.beginReadArray('directories')
356+
directories = []
357+
for j in range(directory_count):
358+
settings.setArrayIndex(j)
359+
directories.append(settings.value('directory'))
360+
settings.endArray()
361+
self._profile_directories[profile_name] = directories
340362
settings.endArray()
341363
settings.endGroup()
364+
self._profile_directories[CONST_DEFAULT_PROFILE] = self._profile_directories.get(CONST_DEFAULT_PROFILE, [])
342365
settings.beginGroup('Ignored Plugins')
343366
plugin_count = settings.beginReadArray('plugins')
344367
for i in range(plugin_count):
@@ -371,12 +394,18 @@ def writeSettings(self, settings):
371394
settings.beginGroup('Plugins')
372395
settings.setValue('donot_show_plugin_errors', self._doNotShowPluginErrors)
373396
settings.setValue('virtualenv_setup_attempted', self._virtualenv_setup_attempted)
374-
settings.beginWriteArray('directories')
375-
directory_index = 0
376-
for directory in self._directories:
377-
settings.setArrayIndex(directory_index)
378-
settings.setValue('directory', directory)
379-
directory_index += 1
397+
settings.setValue(_get_app_profile_key(), self._current_profile)
398+
settings.beginWriteArray('profiles')
399+
profile_index = 0
400+
for profile_name, profile_directories in self._profile_directories.items():
401+
settings.setArrayIndex(profile_index)
402+
settings.setValue('name', profile_name)
403+
settings.beginWriteArray('directories')
404+
for directory_index, directory in enumerate(profile_directories):
405+
settings.setArrayIndex(directory_index)
406+
settings.setValue('directory', directory)
407+
settings.endArray()
408+
profile_index += 1
380409
settings.endArray()
381410
settings.endGroup()
382411
settings.beginGroup('Ignored Plugins')
@@ -557,8 +586,8 @@ def checkForMissingPlugins(self, to_check):
557586
"""
558587
missing_plugins = {}
559588
for plugin in to_check:
560-
if not (plugin in self._database and \
561-
to_check[plugin]['author'] == self._database[plugin]['author'] and \
589+
if not (plugin in self._database and
590+
to_check[plugin]['author'] == self._database[plugin]['author'] and
562591
to_check[plugin]['version'] == self._database[plugin]['version']):
563592
missing_plugins[plugin] = to_check[plugin]
564593

src/mapclient/core/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
You should have received a copy of the GNU General Public License
1818
along with MAP Client. If not, see <http://www.gnu.org/licenses/>..
1919
"""
20+
import hashlib
2021
import json
2122
import logging
2223
import os
@@ -379,3 +380,7 @@ def to_exchangeable_path(input_path):
379380

380381
def to_system_path(input_path):
381382
return str(PurePath(input_path))
383+
384+
385+
def stable_hash(s):
386+
return hashlib.sha256(s.encode()).hexdigest()

src/mapclient/tools/pluginfinder/downloadtodirectorydialog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _download_clicked(self):
6262
'directory. Do you wish to add this directory to the MAP-Client plugin search path?')
6363
if add_dir == QMessageBox.StandardButton.Yes:
6464
self._plugin_directories.append(self._selected_directory)
65-
self._plugin_manager.setDirectories(self._plugin_directories)
65+
self._plugin_manager.set_directories(self._plugin_directories)
6666

6767
answer = QMessageBox.information(self, 'Download Successful', 'The selected plugin was successfully downloaded. '
6868
'You may need to restart the application to pick up newly downloaded plugins.')

src/mapclient/tools/pluginmanager/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from PySide6 import QtWidgets
2+
3+
4+
class EditProfileDialog(QtWidgets.QDialog):
5+
"""
6+
Dialog for editing the name of a plugin profile.
7+
"""
8+
9+
def __init__(self, current_profile_name, parent=None):
10+
super(EditProfileDialog, self).__init__(parent)
11+
self.setWindowTitle("Edit Profile")
12+
self.setMinimumSize(300, 150)
13+
14+
self.layout = QtWidgets.QVBoxLayout(self)
15+
16+
self.label = QtWidgets.QLabel("Enter the new name for the profile:")
17+
self.layout.addWidget(self.label)
18+
19+
self.profileNameInput = QtWidgets.QLineEdit(self)
20+
self.profileNameInput.setText(current_profile_name)
21+
self.layout.addWidget(self.profileNameInput)
22+
23+
self.layout.addSpacerItem(QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum))
24+
25+
self.buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.StandardButton.Ok | QtWidgets.QDialogButtonBox.StandardButton.Cancel)
26+
self.buttonBox.accepted.connect(self.accept)
27+
self.buttonBox.rejected.connect(self.reject)
28+
self.layout.addWidget(self.buttonBox)
29+
30+
def getProfileName(self):
31+
return self.profileNameInput.text().strip()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from PySide6 import QtWidgets
2+
3+
4+
class NewProfileDialog(QtWidgets.QDialog):
5+
def __init__(self, parent=None):
6+
super(NewProfileDialog, self).__init__(parent)
7+
self.setWindowTitle("New Profile")
8+
self.setMinimumSize(300, 150)
9+
10+
self.layout = QtWidgets.QVBoxLayout(self)
11+
12+
self.label = QtWidgets.QLabel("Enter the name for the new profile:")
13+
self.layout.addWidget(self.label)
14+
15+
self.profileNameInput = QtWidgets.QLineEdit(self)
16+
self.layout.addWidget(self.profileNameInput)
17+
18+
self.layout.addSpacerItem(QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum))
19+
20+
self.buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.StandardButton.Ok | QtWidgets.QDialogButtonBox.StandardButton.Cancel)
21+
self.buttonBox.accepted.connect(self.accept)
22+
self.buttonBox.rejected.connect(self.reject)
23+
self.layout.addWidget(self.buttonBox)
24+
25+
def getProfileName(self):
26+
return self.profileNameInput.text().strip()

0 commit comments

Comments
 (0)