Skip to content

Commit 0aefdcb

Browse files
authored
Merge pull request #45 from WhereGroup/ui_improvements
UI improvements
2 parents f2bed94 + 35c5adc commit 0aefdcb

File tree

2 files changed

+110
-30
lines changed

2 files changed

+110
-30
lines changed

profile_manager/profile_manager_dialog.py

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, profile_manager, parent=None):
4949

5050
self.__setup_connections()
5151

52-
# initial population of things on import tab
52+
# making sure that the combo boxes are set up correctly
5353
self.comboBoxNamesSource.currentTextChanged.emit(
5454
self.comboBoxNamesSource.currentText()
5555
)
@@ -84,6 +84,29 @@ def __setup_connections(self):
8484
self.__conditionally_enable_profile_buttons
8585
)
8686

87+
# update import/remove buttons on selection changes
88+
self.treeWidgetSource.itemClicked.connect(
89+
self.__conditionally_enable_import_buttons
90+
)
91+
self.treeWidgetTarget.itemClicked.connect(
92+
self.__conditionally_enable_import_buttons
93+
)
94+
self.list_plugins.itemClicked.connect(
95+
self.__conditionally_enable_import_buttons
96+
)
97+
checkboxes = [
98+
self.bookmark_check,
99+
self.favourites_check,
100+
self.models_check,
101+
self.scripts_check,
102+
self.styles_check,
103+
self.expressions_check,
104+
self.checkBox_checkAll,
105+
self.customization_check,
106+
]
107+
for checkbox in checkboxes:
108+
checkbox.stateChanged.connect(self.__conditionally_enable_import_buttons)
109+
87110
def get_list_selection_profile_name(self) -> Optional[str]:
88111
"""Get selected profile name from list
89112
@@ -158,13 +181,34 @@ def export_qdt_handler(self) -> None:
158181
def __conditionally_enable_import_buttons(self):
159182
source = self.__profile_manager.source_profile_name
160183
target = self.__profile_manager.target_profile_name
161-
if source == target:
162-
self.removeThingsButton.setEnabled(True)
184+
any_thing_is_selected = any(
185+
[
186+
self.__selected_plugins(),
187+
self.__selected_data_sources(),
188+
self.bookmark_check.isChecked(),
189+
self.favourites_check.isChecked(),
190+
self.models_check.isChecked(),
191+
self.scripts_check.isChecked(),
192+
self.styles_check.isChecked(),
193+
self.expressions_check.isChecked(),
194+
self.customization_check.isChecked(),
195+
]
196+
)
197+
198+
if not any_thing_is_selected:
199+
# Nothing to do? Don't enable any of the buttons.
163200
self.importThingsButton.setEnabled(False)
164-
elif source is None and target is not None:
165201
self.removeThingsButton.setEnabled(False)
202+
elif source is None:
203+
# Both importing and deleting need a *source* profile to be selected.
166204
self.importThingsButton.setEnabled(False)
167-
elif source is not None and target is None:
205+
self.removeThingsButton.setEnabled(False)
206+
elif source == target and any_thing_is_selected:
207+
# Don't allow importing into itself, but allow deletion of the selected things in the source profile.
208+
self.importThingsButton.setEnabled(False)
209+
self.removeThingsButton.setEnabled(True)
210+
elif source is not None and target is None and any_thing_is_selected:
211+
# Only allow deletion of the selected things in the source profile.
168212
self.importThingsButton.setEnabled(False)
169213
self.removeThingsButton.setEnabled(True)
170214
else:
@@ -215,7 +259,6 @@ def __conditionally_enable_profile_buttons(self):
215259

216260
def __on_source_profile_changed(self, profile_name: str):
217261
self.__profile_manager.change_source_profile(profile_name)
218-
self.__conditionally_enable_import_buttons()
219262

220263
if profile_name is None:
221264
self.treeWidgetSource.clear()
@@ -227,10 +270,10 @@ def __on_source_profile_changed(self, profile_name: str):
227270
self.__update_plugins_widget(
228271
"source", self.__profile_manager.source_plugins
229272
)
273+
self.__conditionally_enable_import_buttons()
230274

231275
def __on_target_profile_changed(self, profile_name: str):
232276
self.__profile_manager.change_target_profile(profile_name)
233-
self.__conditionally_enable_import_buttons()
234277

235278
if profile_name is None:
236279
self.treeWidgetTarget.clear()
@@ -242,6 +285,7 @@ def __on_target_profile_changed(self, profile_name: str):
242285
self.__update_plugins_widget(
243286
"target", self.__profile_manager.target_plugins
244287
)
288+
self.__conditionally_enable_import_buttons()
245289

246290
def populate_profile_listings(self):
247291
"""Populates the main list as well as the comboboxes with available profile names.
@@ -250,12 +294,9 @@ def populate_profile_listings(self):
250294
251295
TODO this docstring seems not correct anymore.
252296
TODO how//where IS the profile model updated?
253-
TODO document WHY blocksignals is used
254297
"""
255-
self.comboBoxNamesSource.blockSignals(True)
256298
active_profile_name = Path(QgsApplication.qgisSettingsDirPath()).name
257299
self.comboBoxNamesSource.setCurrentText(active_profile_name)
258-
self.comboBoxNamesSource.blockSignals(False)
259300

260301
self.__conditionally_enable_profile_buttons()
261302

@@ -303,38 +344,46 @@ def __populate_plugins_list(
303344
for item in items:
304345
plugins_widget.addItem(item)
305346

306-
def __set_checkstates(self, checkstate: Qt.CheckState):
347+
def __set_all_checkstates(self, checkstate: Qt.CheckState):
348+
"""Sets the specified checkstate for all enabled checkboxes."""
307349
for item in self.treeWidgetSource.findItems(
308350
"", Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchRecursive
309351
):
310-
item.setCheckState(0, checkstate)
352+
if item.flags() & Qt.ItemFlag.ItemIsEnabled:
353+
item.setCheckState(0, checkstate)
311354

312355
for item in self.list_plugins.findItems(
313356
"", Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchRecursive
314357
):
315-
item.setCheckState(checkstate)
316-
317-
self.bookmark_check.setCheckState(checkstate)
318-
self.favourites_check.setCheckState(checkstate)
319-
self.models_check.setCheckState(checkstate)
320-
self.scripts_check.setCheckState(checkstate)
321-
self.styles_check.setCheckState(checkstate)
322-
self.expressions_check.setCheckState(checkstate)
323-
self.checkBox_checkAll.setCheckState(checkstate)
324-
self.customization_check.setCheckState(checkstate)
358+
if item.flags() & Qt.ItemFlag.ItemIsEnabled:
359+
item.setCheckState(checkstate)
360+
361+
checkboxes = [
362+
self.bookmark_check,
363+
self.favourites_check,
364+
self.models_check,
365+
self.scripts_check,
366+
self.styles_check,
367+
self.expressions_check,
368+
self.checkBox_checkAll,
369+
self.customization_check,
370+
]
371+
for checkbox in checkboxes:
372+
if checkbox.isEnabled():
373+
checkbox.setCheckState(checkstate)
325374

326375
def __toggle_all_items(self):
327-
"""Checks/Unchecks every checkbox in the gui"""
376+
"""Checks/Unchecks every enabled checkbox in the gui"""
328377
if self.__everything_is_checked:
329378
checkstate = Qt.CheckState.Unchecked
330379
else:
331380
checkstate = Qt.CheckState.Checked
332-
self.__set_checkstates(checkstate)
381+
self.__set_all_checkstates(checkstate)
333382
self.__everything_is_checked = not self.__everything_is_checked
334383

335384
def __uncheck_everything(self):
336385
"""Unchecks every checkbox"""
337-
self.__set_checkstates(Qt.CheckState.Unchecked)
386+
self.__set_all_checkstates(Qt.CheckState.Unchecked)
338387
self.__everything_is_checked = False
339388

340389
def __update_data_sources_widget(

profile_manager/profiles/profile_handler.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import errno
12
import re
23
from os import rename
34
from pathlib import Path
45
from shutil import copytree, rmtree
56
from sys import platform
67

7-
from qgis.core import QgsApplication, QgsUserProfileManager
8+
from qgis.core import QgsApplication, QgsError, QgsUserProfileManager
89

910
from profile_manager.profiles.utils import qgis_profiles_path
1011

@@ -19,8 +20,18 @@ def create_profile(profile_name: str):
1920
if not re.match(VALID_PROJECT_NAME_REGEX, profile_name):
2021
raise ValueError("Invalid profile name")
2122

23+
# Unfortunately QgsUserProfileManager.createUserProfile() does not notice if
24+
# the target directory already exists as of QGIS 3.40.3...
25+
# So we have our own check here:
26+
if Path(qgis_profiles_path() / profile_name).exists():
27+
raise ValueError("Target directory already exists")
28+
2229
qgs_profile_manager = QgsUserProfileManager(str(qgis_profiles_path()))
23-
qgs_profile_manager.createUserProfile(profile_name)
30+
error: QgsError = qgs_profile_manager.createUserProfile(profile_name)
31+
if not error.isEmpty():
32+
raise Exception(
33+
f"QGIS could not create the new user profile: {error.summary()}"
34+
)
2435

2536
# Right now there is only the profile directory and the qgis.db in its root.
2637
# We want to be able to write things to the profile's QGIS3.ini file so:
@@ -44,7 +55,13 @@ def remove_profile(profile_name: str):
4455
raise ValueError("Cannot remove the profile that is currently active")
4556

4657
profile_path = qgis_profiles_path() / profile_name
47-
rmtree(profile_path)
58+
try:
59+
rmtree(profile_path)
60+
except OSError as e:
61+
if e.errno == errno.EEXIST:
62+
raise ValueError("A profile with the same name already exists.")
63+
else:
64+
raise
4865

4966

5067
def copy_profile(source_profile_name: str, target_profile_name: str):
@@ -62,7 +79,13 @@ def copy_profile(source_profile_name: str, target_profile_name: str):
6279
source_profile_path = qgis_profiles_path() / source_profile_name
6380
profile_path = qgis_profiles_path() / target_profile_name
6481

65-
copytree(source_profile_path, profile_path)
82+
try:
83+
copytree(source_profile_path, profile_path)
84+
except OSError as e:
85+
if e.errno == errno.EEXIST:
86+
raise ValueError("A profile with the same name already exists.")
87+
else:
88+
raise
6689

6790

6891
def rename_profile(old_profile_name: str, new_profile_name: str):
@@ -71,6 +94,8 @@ def rename_profile(old_profile_name: str, new_profile_name: str):
7194
raise ValueError("Empty old profile name provided")
7295
if not old_profile_name:
7396
raise ValueError("Empty new profile name provided")
97+
if old_profile_name == new_profile_name:
98+
raise ValueError("New name is identical to old name")
7499
if not re.match(VALID_PROJECT_NAME_REGEX, old_profile_name):
75100
raise ValueError("Invalid old profile name")
76101
if not re.match(VALID_PROJECT_NAME_REGEX, new_profile_name):
@@ -81,4 +106,10 @@ def rename_profile(old_profile_name: str, new_profile_name: str):
81106
profile_before_change = qgis_profiles_path() / old_profile_name
82107
profile_after_change = qgis_profiles_path() / new_profile_name
83108

84-
rename(profile_before_change, profile_after_change)
109+
try:
110+
rename(profile_before_change, profile_after_change)
111+
except OSError as e:
112+
if e.errno == errno.ENOTEMPTY:
113+
raise ValueError("A profile with the same name already exists.")
114+
else:
115+
raise

0 commit comments

Comments
 (0)