diff --git a/picard/script/__init__.py b/picard/script/__init__.py index ff8db2bef..b09d68a77 100644 --- a/picard/script/__init__.py +++ b/picard/script/__init__.py @@ -137,7 +137,7 @@ class PicardScript(): # Base class developed to support future tagging script class as possible replacement for currently used tuples in config.setting["list_of_scripts"]. TYPE = PicardScriptType.BASE - OUTPUT_FIELDS = {'title', 'script', 'script_language_version'} + OUTPUT_FIELDS = {'title', 'script', 'script_language_version', 'id'} # Don't automatically trigger changing the `script_last_updated` property when updating these properties. _last_updated_ignore_list = {'last_updated', 'readonly', 'deletable', 'id'} @@ -153,7 +153,7 @@ class PicardScript(): """ self.title = title if title else DEFAULT_SCRIPT_NAME self.script = script - if id is None: + if not id: self._set_new_id() else: self.id = id @@ -270,7 +270,7 @@ class PicardScript(): # TODO: Enable once PyYAML requirement resolved with Python 3.8 # # @classmethod - # def create_from_yaml(cls, yaml_string): + # def create_from_yaml(cls, yaml_string, create_new_id=True): # """Creates an instance based on the contents of the YAML string provided. # Properties in the YAML string that are not found in the script object are ignored. @@ -287,15 +287,18 @@ class PicardScript(): # if 'title' not in yaml_dict or 'script' not in yaml_dict: # raise ScriptImportError(N_('Invalid script package')) # new_object._update_from_dict(yaml_dict) + # if create_new_id or not new_object['id']: + # new_object._set_new_id() # return new_object @classmethod - def create_from_json(cls, json_string): + def create_from_json(cls, json_string, create_new_id=True): """Creates an instance based on the contents of the JSON string provided. Properties in the JSON string that are not found in the script object are ignored. Args: json_string (str): JSON string containing the property settings. + create_new_id (bool): Do not use the existing id. Defaults to True. Returns: object: An instance of the class, populated from the property settings in the JSON string. @@ -305,6 +308,8 @@ class PicardScript(): new_object.update_from_json(json_string) if not (new_object['title'] and new_object['script']): raise ScriptImportError(N_('Invalid script package')) + if create_new_id or not new_object['id']: + new_object._set_new_id() return new_object def update_from_json(self, json_string): @@ -325,7 +330,7 @@ class FileNamingScript(PicardScript): """Picard file naming script class """ TYPE = PicardScriptType.FILENAMING - OUTPUT_FIELDS = {'title', 'script', 'author', 'description', 'license', 'version', 'last_updated', 'script_language_version'} + OUTPUT_FIELDS = {'title', 'script', 'author', 'description', 'license', 'version', 'last_updated', 'script_language_version', 'id'} def __init__( self, @@ -379,6 +384,7 @@ def get_file_naming_script_presets(): return _("Preset %d: %s") % (number, _(title)) yield FileNamingScript( + id="Preset 1", title=preset_title(1, N_("Default file naming script")), script=DEFAULT_FILE_NAMING_FORMAT, readonly=True, @@ -392,6 +398,7 @@ def get_file_naming_script_presets(): ) yield FileNamingScript( + id="Preset 2", title=preset_title(2, N_("[album artist]/[album]/[track #]. [title]")), script="%albumartist%/\n" "%album%/\n" @@ -407,6 +414,7 @@ def get_file_naming_script_presets(): ) yield FileNamingScript( + id="Preset 3", title=preset_title(3, N_("[album artist]/[album]/[disc and track #] [artist] - [title]")), script="$if2(%albumartist%,%artist%)/\n" "$if(%albumartist%,%album%/,)\n" diff --git a/picard/ui/scripteditor.py b/picard/ui/scripteditor.py index 0081885ed..23f9e5cc1 100644 --- a/picard/ui/scripteditor.py +++ b/picard/ui/scripteditor.py @@ -247,6 +247,11 @@ class ScriptEditorPage(PicardDialog): "file_naming_scripts", [], ), + TextOption( + "setting", + "selected_file_naming_script_id", + "", + ), ] signal_save = QtCore.pyqtSignal() @@ -277,17 +282,19 @@ class ScriptEditorPage(PicardDialog): self.setWindowModality(QtCore.Qt.WindowModal) self.setWindowTitle(self.TITLE) self.displaying = False + self.loading = True self.ui = Ui_ScriptEditor() self.ui.setupUi(self) self.ui.example_filename_sample_files_button.setToolTip(_(self.examples.tooltip_text) % self.examples.max_samples) + self.ui.label.setWordWrap(False) self.installEventFilter(self) self.ui.file_naming_editor_new.clicked.connect(self.new_script) self.ui.file_naming_editor_save.clicked.connect(self.save_script) self.ui.file_naming_editor_copy.clicked.connect(self.copy_script) - self.ui.file_naming_editor_select.clicked.connect(self.save_selected_script) + self.ui.file_naming_editor_close.clicked.connect(self.close_window) self.ui.file_naming_editor_delete.clicked.connect(self.delete_script) self.ui.script_details.clicked.connect(self.view_script_details) @@ -311,9 +318,11 @@ class ScriptEditorPage(PicardDialog): config = get_config() self.naming_scripts = config.setting["file_naming_scripts"] - self.populate_script_selector() - self.ui.preset_naming_scripts.setCurrentIndex(0) + self.selected_script_id = config.setting["selected_file_naming_script_id"] + self.selected_script_index = 0 + idx = self.populate_script_selector() self.ui.preset_naming_scripts.currentIndexChanged.connect(self.select_script) + self.ui.preset_naming_scripts.setCurrentIndex(idx) self.select_script() self.synchronize_vertical_scrollbars((self.ui.example_filename_before, self.ui.example_filename_after)) @@ -322,6 +331,7 @@ class ScriptEditorPage(PicardDialog): self.examples_current_row = -1 self.load() + self.loading = False @staticmethod def synchronize_vertical_scrollbars(widgets): @@ -354,31 +364,59 @@ class ScriptEditorPage(PicardDialog): self.update_examples() return False + def close_window(self): + """Close the window. + """ + self.close() + + def closeEvent(self, event): + """Custom close event handler to check for unsaved changes. + """ + if self.unsaved_changes_confirmation(): + event.accept() + else: + event.ignore() + def populate_script_selector(self): """Populate the script selection combo box. + + Returns: + int: The index of the selected script in the combo box. """ + if not self.selected_script_id: + script_item = FileNamingScript( + script=get_config().setting["file_naming_format"], + title=_("Primary file naming script"), + readonly=False, + deletable=True, + ) + self.naming_scripts.insert(0, script_item.to_json()) + self.selected_script_id = script_item['id'] + + idx = 0 self.ui.preset_naming_scripts.blockSignals(True) self.ui.preset_naming_scripts.clear() - script_item = FileNamingScript( - script=get_config().setting["file_naming_format"], - title=_("Current file naming script saved in configuration"), - readonly=False, - deletable=False, - id="current" - ) - self.set_script(script_item.script) - self.ui.preset_naming_scripts.addItem(self.SCRIPT_TITLE_SYSTEM % script_item.title, script_item) - for script_json in self.naming_scripts: - script_item = FileNamingScript().create_from_json(script_json) - if script_item['title']: + for i in range(len(self.naming_scripts)): + try: + script_item = FileNamingScript().create_from_json(self.naming_scripts[i], create_new_id=False) + except ScriptImportError: + pass + else: + self.naming_scripts[i] = script_item.to_json() # Ensure scripts are stored with id codes self.ui.preset_naming_scripts.addItem(self.SCRIPT_TITLE_USER % script_item.title, script_item) + if script_item['id'] == self.selected_script_id: + idx = i for script_item in get_file_naming_script_presets(): title = script_item.title self.ui.preset_naming_scripts.addItem(title, script_item) + if script_item['id'] == self.selected_script_id: + idx = self.ui.preset_naming_scripts.count() - 1 self.ui.preset_naming_scripts.blockSignals(False) + self.update_scripts_list() + return idx def toggle_documentation(self): """Toggle the display of the scripting documentation sidebar. @@ -395,6 +433,15 @@ class ScriptEditorPage(PicardDialog): details_page.raise_() details_page.activateWindow() + def has_changed(self): + """Check if the current script has pending edits to the title or script that have not been saved. + + Returns: + bool: True if there are unsaved changes, otherwise false. + """ + script_item = self.ui.preset_naming_scripts.itemData(self.selected_script_index) + return self.ui.script_title.text().strip() != script_item['title'] or self.get_script() != script_item['script'] + def update_from_details(self): """Update the script selection combo box and script list after updates from the script details dialog. """ @@ -409,8 +456,9 @@ class ScriptEditorPage(PicardDialog): script_item (FileNamingScript): File naming scrip to insert. """ self.ui.preset_naming_scripts.blockSignals(True) - self.ui.preset_naming_scripts.insertItem(1, self.SCRIPT_TITLE_USER % script_item.title, script_item) - self.ui.preset_naming_scripts.setCurrentIndex(1) + idx = len(self.naming_scripts) + self.ui.preset_naming_scripts.insertItem(idx, self.SCRIPT_TITLE_USER % script_item.title, script_item) + self.ui.preset_naming_scripts.setCurrentIndex(idx) self.ui.preset_naming_scripts.blockSignals(False) self.update_scripts_list() self.select_script() @@ -418,16 +466,24 @@ class ScriptEditorPage(PicardDialog): def new_script(self): """Add a new (empty) script to the script selection combo box and script list. """ - script_item = FileNamingScript(script='$noop()') - self._insert_item(script_item) + if self.unsaved_changes_confirmation(): + script_item = FileNamingScript(script='$noop()') + self._insert_item(script_item) def copy_script(self): """Add a copy of the script as a new editable script to the script selection combo box and script list. """ - selected = self.ui.preset_naming_scripts.currentIndex() - script_item = self.ui.preset_naming_scripts.itemData(selected) - new_item = script_item.copy() - self._insert_item(new_item) + if self.unsaved_changes_confirmation(): + selected = self.ui.preset_naming_scripts.currentIndex() + script_item = self.ui.preset_naming_scripts.itemData(selected) + new_item = script_item.copy() + self._insert_item(new_item) + + def update_script_in_settings(self, script_item): + self.signal_save.emit() + config = get_config() + config.setting["file_naming_format"] = script_item['script'] + config.setting["selected_file_naming_script_id"] = self.selected_script_id def update_scripts_list(self): """Refresh the script list in the settings based on the contents of the script selection combo box. @@ -435,7 +491,7 @@ class ScriptEditorPage(PicardDialog): self.naming_scripts = [] for idx in range(self.ui.preset_naming_scripts.count()): script_item = self.ui.preset_naming_scripts.itemData(idx) - # Only add items that can be removed -- no presets or the current file naming script + # Only add items that can be removed -- no presets if script_item.deletable: self.naming_scripts.append(script_item.to_json()) config = get_config() @@ -450,14 +506,33 @@ class ScriptEditorPage(PicardDialog): selected = self.ui.preset_naming_scripts.currentIndex() return self.ui.preset_naming_scripts.itemData(selected) + def unsaved_changes_confirmation(self): + """Check if there are unsaved changes and as the user to confirm the action resulting in their loss. + + Returns: + bool: True if no unsaved changes or user confirms the action, otherwise False. + """ + if not self.loading and self.has_changed() and not self.confirmation_dialog( + _("There are unsaved changes to the current script. Do you want to continue and lose these changes?") + ): + self.ui.preset_naming_scripts.blockSignals(True) + self.ui.preset_naming_scripts.setCurrentIndex(self.selected_script_index) + self.ui.preset_naming_scripts.blockSignals(False) + return False + return True + def select_script(self): """Load the current script from the combo box into the editor. """ - script_item = self.get_selected_item() - self.ui.script_title.setText(str(script_item.title).strip()) - self.set_script(script_item.script) - self.set_button_states() - self.update_examples() + if self.unsaved_changes_confirmation(): + script_item = self.get_selected_item() + self.ui.script_title.setText(str(script_item.title).strip()) + self.set_script(script_item.script) + self.selected_script_id = script_item['id'] + self.selected_script_index = self.ui.preset_naming_scripts.currentIndex() + self.update_script_in_settings(script_item) + self.set_button_states() + self.update_examples() def update_combo_box_item(self, idx, script_item): """Update the title and item data for the specified script selection combo box item. @@ -468,6 +543,8 @@ class ScriptEditorPage(PicardDialog): """ self.ui.preset_naming_scripts.setItemData(idx, script_item) self.ui.preset_naming_scripts.setItemText(idx, self.SCRIPT_TITLE_USER % script_item['title']) + self.update_script_in_settings(script_item) + self.update_scripts_list() def set_button_states(self, save_enabled=True): """Set the button states based on the readonly and deletable attributes of the currently selected @@ -484,7 +561,6 @@ class ScriptEditorPage(PicardDialog): self.ui.file_naming_format.setReadOnly(script_item.readonly) self.ui.file_naming_editor_save.setEnabled(save_enabled and not script_item.readonly) self.ui.file_naming_editor_copy.setEnabled(save_enabled) - self.ui.file_naming_editor_select.setEnabled(save_enabled) self.ui.file_naming_editor_delete.setEnabled(script_item.deletable and save_enabled) self.ui.import_script.setEnabled(save_enabled) self.ui.export_script.setEnabled(save_enabled) @@ -526,23 +602,15 @@ class ScriptEditorPage(PicardDialog): """Saves changes to the current script to the script list and combo box item. """ selected = self.ui.preset_naming_scripts.currentIndex() - if selected == 0: - self.signal_save.emit() - else: - title = str(self.ui.script_title.text()).strip() - if title: - script_item = self.ui.preset_naming_scripts.itemData(selected) - script_item.title = title - script_item.script = self.get_script() - self.update_combo_box_item(selected, script_item) - self.update_scripts_list() - else: - self.display_error(OptionsCheckError(_("Error"), _("The script title must not be empty."))) - - def save_selected_script(self): - """Emits a `save` signal to trigger appropriate save action in the parent object. - """ self.signal_save.emit() + title = str(self.ui.script_title.text()).strip() + if title: + script_item = self.ui.preset_naming_scripts.itemData(selected) + script_item.title = title + script_item.script = self.get_script() + self.update_combo_box_item(selected, script_item) + else: + self.display_error(OptionsCheckError(_("Error"), _("The script title must not be empty."))) def get_script(self): """Provides the text of the file naming script currently loaded into the editor. @@ -553,7 +621,7 @@ class ScriptEditorPage(PicardDialog): return str(self.ui.file_naming_format.toPlainText()).strip() def set_script(self, script_text): - """Sets the text of the file naming script into the editor. + """Sets the text of the file naming script into the editor and settings. Args: script_text (str): File naming script text to set in the editor. @@ -634,6 +702,9 @@ class ScriptEditorPage(PicardDialog): FILE_ERROR_IMPORT = N_('Error importing "%s". %s.') FILE_ERROR_DECODE = N_('Error decoding "%s". %s.') + if not self.unsaved_changes_confirmation(): + return + dialog_title = _("Import Script File") dialog_file_types = self.FILE_TYPE_PACKAGE + ";;" + self.FILE_TYPE_SCRIPT + ";;" + self.FILE_TYPE_ALL options = QtWidgets.QFileDialog.Options() @@ -703,13 +774,10 @@ class ScriptEditorPage(PicardDialog): dialog.exec_() def load(self): - """Loads the file naming script from the configuration settings. + """Loads the file naming script from the selected combo box item. """ self.toggle_wordwrap() self.toggle_documentation() - self.ui.preset_naming_scripts.blockSignals(True) - self.ui.preset_naming_scripts.setCurrentIndex(0) - self.ui.preset_naming_scripts.blockSignals(False) self.select_script() def check_formats(self): diff --git a/picard/ui/ui_scripteditor.py b/picard/ui/ui_scripteditor.py index 904d74737..1e43b0bc5 100644 --- a/picard/ui/ui_scripteditor.py +++ b/picard/ui/ui_scripteditor.py @@ -46,12 +46,13 @@ class Ui_ScriptEditor(object): self.horizontalLayout_2.setContentsMargins(-1, -1, -1, 0) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.label = QtWidgets.QLabel(self.frame_4) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) self.label.setSizePolicy(sizePolicy) self.label.setMinimumSize(QtCore.QSize(0, 0)) + self.label.setIndent(0) self.label.setObjectName("label") self.horizontalLayout_2.addWidget(self.label) self.preset_naming_scripts = QtWidgets.QComboBox(self.frame_4) @@ -155,6 +156,7 @@ class Ui_ScriptEditor(object): spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem) self.script_details = QtWidgets.QPushButton(self.frame_4) + self.script_details.setStatusTip("") self.script_details.setObjectName("script_details") self.horizontalLayout_4.addWidget(self.script_details) self.import_script = QtWidgets.QPushButton(self.frame_4) @@ -163,21 +165,21 @@ class Ui_ScriptEditor(object): self.export_script = QtWidgets.QPushButton(self.frame_4) self.export_script.setObjectName("export_script") self.horizontalLayout_4.addWidget(self.export_script) - self.file_naming_editor_delete = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_delete.setObjectName("file_naming_editor_delete") - self.horizontalLayout_4.addWidget(self.file_naming_editor_delete) - self.file_naming_editor_save = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_save.setObjectName("file_naming_editor_save") - self.horizontalLayout_4.addWidget(self.file_naming_editor_save) self.file_naming_editor_new = QtWidgets.QPushButton(self.frame_4) self.file_naming_editor_new.setObjectName("file_naming_editor_new") self.horizontalLayout_4.addWidget(self.file_naming_editor_new) self.file_naming_editor_copy = QtWidgets.QPushButton(self.frame_4) self.file_naming_editor_copy.setObjectName("file_naming_editor_copy") self.horizontalLayout_4.addWidget(self.file_naming_editor_copy) - self.file_naming_editor_select = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_select.setObjectName("file_naming_editor_select") - self.horizontalLayout_4.addWidget(self.file_naming_editor_select) + self.file_naming_editor_delete = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_delete.setObjectName("file_naming_editor_delete") + self.horizontalLayout_4.addWidget(self.file_naming_editor_delete) + self.file_naming_editor_save = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_save.setObjectName("file_naming_editor_save") + self.horizontalLayout_4.addWidget(self.file_naming_editor_save) + self.file_naming_editor_close = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_close.setObjectName("file_naming_editor_close") + self.horizontalLayout_4.addWidget(self.file_naming_editor_close) self.verticalLayout_5.addLayout(self.horizontalLayout_4) self.groupBox = QtWidgets.QGroupBox(self.splitter_between_editor_and_examples) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) @@ -236,7 +238,7 @@ class Ui_ScriptEditor(object): def retranslateUi(self, ScriptEditor): _translate = QtCore.QCoreApplication.translate - self.label.setText(_("File naming scripts:")) + self.label.setText(_("Select the file naming script to use:")) self.preset_naming_scripts.setToolTip(_("Select the file naming script to load into the editor")) self.label_2.setText(_("Title:")) self.file_naming_word_wrap.setToolTip(_("Word wrap long lines in the editor")) @@ -245,22 +247,22 @@ class Ui_ScriptEditor(object): self.show_documentation.setText(_("Show Documentation")) self.example_filename_sample_files_button.setToolTip(_("Up to 10 items chosen at random from files selected in the main window")) self.example_filename_sample_files_button.setText(_("Reload Examples")) - self.script_details.setStatusTip(_("Display the details for the currently selected script")) + self.script_details.setToolTip(_("Display the details for the script")) self.script_details.setText(_("Details")) - self.import_script.setToolTip(_("Import a file to replace the current script")) + self.import_script.setToolTip(_("Import a file as a new script")) self.import_script.setText(_("Import")) - self.export_script.setToolTip(_("Export the current script to a file")) + self.export_script.setToolTip(_("Export the script to a file")) self.export_script.setText(_("Export")) - self.file_naming_editor_delete.setToolTip(_("Delete the currently selected script")) - self.file_naming_editor_delete.setText(_("Delete")) - self.file_naming_editor_save.setToolTip(_("Save changes to the current script")) - self.file_naming_editor_save.setText(_("Save")) self.file_naming_editor_new.setToolTip(_("Create a new file naming script")) self.file_naming_editor_new.setText(_("New")) self.file_naming_editor_copy.setToolTip(_("Save a copy of the script as a new script")) self.file_naming_editor_copy.setText(_("Copy")) - self.file_naming_editor_select.setToolTip(_("Select the current script to use as the file naming script")) - self.file_naming_editor_select.setText(_("Select")) + self.file_naming_editor_delete.setToolTip(_("Delete the script")) + self.file_naming_editor_delete.setText(_("Delete")) + self.file_naming_editor_save.setToolTip(_("Save changes to the script")) + self.file_naming_editor_save.setText(_("Save")) + self.file_naming_editor_close.setToolTip(_("Close the script editor")) + self.file_naming_editor_close.setText(_("Close")) self.groupBox.setTitle(_("Files will be named like this")) self.example_filename_before_label.setText(_("Before")) self.example_filename_after_label.setText(_("After")) diff --git a/test/test_scriptclasses.py b/test/test_scriptclasses.py index f670dcad3..28fdda427 100644 --- a/test/test_scriptclasses.py +++ b/test/test_scriptclasses.py @@ -57,7 +57,7 @@ class ScriptClassesTest(PicardTestCase): self.assertEqual(test_script['id'], '12345') self.assertEqual(test_script.last_updated, '2021-04-26') self.assertEqual(test_script['last_updated'], '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') + self.assertEqual(test_script.to_json(), '{"id": "12345", "script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') def test_script_object_2(self): # Check updating values directly so as not to modify `last_updated`. @@ -105,7 +105,7 @@ class ScriptClassesTest(PicardTestCase): test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_language_version='1.0') test_script.update_script_setting(description='Updated description') self.assertEqual(test_script['last_updated'], '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') + self.assertEqual(test_script.to_json(), '{"id": "12345", "script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') with self.assertRaises(AttributeError): print(test_script.description) @@ -114,7 +114,7 @@ class ScriptClassesTest(PicardTestCase): test_script = PicardScript(title='Script 1', script='Script text', id='12345', last_updated='2021-04-26', script_language_version='1.0') test_script.update_from_json('{"description": "Updated description"}') self.assertEqual(test_script['last_updated'], '2021-04-26') - self.assertEqual(test_script.to_json(), '{"script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') + self.assertEqual(test_script.to_json(), '{"id": "12345", "script": "Script text", "script_language_version": "1.0", "title": "Script 1"}') with self.assertRaises(AttributeError): print(test_script.description) @@ -145,27 +145,31 @@ class ScriptClassesTest(PicardTestCase): self.assertEqual(test_script['script'], 'Script text') self.assertEqual(test_script.author, 'Script author') self.assertEqual(test_script['author'], 'Script author') - self.assertEqual(test_script.to_json(), - '{' - '"author": "Script author", ' - '"description": "Script description", ' - '"last_updated": "2021-04-26", ' - '"license": "", ' - '"script": "Script text", ' - '"script_language_version": "1.0", ' - '"title": "Script 1", ' - '"version": ""' - '}' - ) - self.assertEqual(test_script.to_json(indent=4), - '{\n' - ' "author": "Script author",\n' - ' "description": "Script description",\n' - ' "last_updated": "2021-04-26",\n' - ' "license": "",\n' - ' "script": "Script text",\n' - ' "script_language_version": "1.0",\n' - ' "title": "Script 1",\n' - ' "version": ""\n' - '}' - ) + self.assertEqual( + test_script.to_json(), + '{' + '"author": "Script author", ' + '"description": "Script description", ' + '"id": "12345", ' + '"last_updated": "2021-04-26", ' + '"license": "", ' + '"script": "Script text", ' + '"script_language_version": "1.0", ' + '"title": "Script 1", ' + '"version": ""' + '}' + ) + self.assertEqual( + test_script.to_json(indent=4), + '{\n' + ' "author": "Script author",\n' + ' "description": "Script description",\n' + ' "id": "12345",\n' + ' "last_updated": "2021-04-26",\n' + ' "license": "",\n' + ' "script": "Script text",\n' + ' "script_language_version": "1.0",\n' + ' "title": "Script 1",\n' + ' "version": ""\n' + '}' + ) diff --git a/ui/scripteditor.ui b/ui/scripteditor.ui index 018323244..9ddaf6077 100644 --- a/ui/scripteditor.ui +++ b/ui/scripteditor.ui @@ -71,7 +71,7 @@ - + 0 0 @@ -83,7 +83,10 @@ - File naming scripts: + Select the file naming script to use: + + + 0 @@ -340,8 +343,11 @@ + + Display the details for the script + - Display the details for the currently selected script + Details @@ -351,7 +357,7 @@ - Import a file to replace the current script + Import a file as a new script Import @@ -361,33 +367,13 @@ - Export the current script to a file + Export the script to a file Export - - - - Delete the currently selected script - - - Delete - - - - - - - Save changes to the current script - - - Save - - - @@ -409,12 +395,32 @@ - + - Select the current script to use as the file naming script + Delete the script - Select + Delete + + + + + + + Save changes to the script + + + Save + + + + + + + Close the script editor + + + Close