Skip to content

Commit

Permalink
Improve line edits
Browse files Browse the repository at this point in the history
We now hook to textEdited signal instead of editingFinished which
makes editing feel more "fuild"

Also fixed minor issues such as wrong tab order in some windows/widgets.

Re spine-tools/Spine-Toolbox#2629
  • Loading branch information
soininen committed Mar 1, 2024
1 parent 569fa28 commit 69a4673
Show file tree
Hide file tree
Showing 41 changed files with 699 additions and 307 deletions.
75 changes: 47 additions & 28 deletions spine_items/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,54 @@


class UpdateCancelOnErrorCommand(SpineToolboxCommand):
def __init__(self, project_item, cancel_on_error):
def __init__(self, item_name, cancel_on_error, project):
"""Command to update Importer, Exporter, and Merger cancel on error setting.
Args:
project_item (ProjectItem): Item
item_name (str): Item's name
cancel_on_error (bool): New setting
project (SpineToolboxProject): project
"""
super().__init__()
self._project_item = project_item
self._item_name = item_name

Check warning on line 27 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L27

Added line #L27 was not covered by tests
self._redo_cancel_on_error = cancel_on_error
self._undo_cancel_on_error = not cancel_on_error
self.setText(f"change {project_item.name} cancel on error setting")
self._project = project
self.setText(f"change {item_name} cancel on error setting")

Check warning on line 31 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L30-L31

Added lines #L30 - L31 were not covered by tests

def redo(self):
self._project_item.set_cancel_on_error(self._redo_cancel_on_error)
item = self._project.get_item(self._item_name)
item.set_cancel_on_error(self._redo_cancel_on_error)

Check warning on line 35 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L34-L35

Added lines #L34 - L35 were not covered by tests

def undo(self):
self._project_item.set_cancel_on_error(self._undo_cancel_on_error)
item = self._project.get_item(self._item_name)
item.set_cancel_on_error(self._undo_cancel_on_error)

Check warning on line 39 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L38-L39

Added lines #L38 - L39 were not covered by tests


class UpdateOnConflictCommand(SpineToolboxCommand):
def __init__(self, project_item, on_conflict):
def __init__(self, item_name, on_conflict, project):
"""Command to update Importer and Merger 'on conflict' setting.
Args:
project_item (ProjectItem): Item
item_name (str): Item's name
on_conflict (str): New setting
project (SpineToolboxProject): project
"""
super().__init__()
self._project_item = project_item
self._item_name = item_name

Check warning on line 52 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L52

Added line #L52 was not covered by tests
self._redo_on_conflict = on_conflict
self._undo_on_conflict = self._project_item.on_conflict
self.setText(f"change {project_item.name} on conflict setting")
project_item = project.get_item(item_name)
self._undo_on_conflict = project_item.on_conflict
self._project = project
self.setText(f"change {item_name} on conflict setting")

Check warning on line 57 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L54-L57

Added lines #L54 - L57 were not covered by tests

def redo(self):
self._project_item.set_on_conflict(self._redo_on_conflict)
item = self._project.get_item(self._item_name)
item.set_on_conflict(self._redo_on_conflict)

Check warning on line 61 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L60-L61

Added lines #L60 - L61 were not covered by tests

def undo(self):
self._project_item.set_on_conflict(self._undo_on_conflict)
item = self._project.get_item(self._item_name)
item.set_on_conflict(self._undo_on_conflict)

Check warning on line 65 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L64-L65

Added lines #L64 - L65 were not covered by tests


class ChangeItemSelectionCommand(SpineToolboxCommand):
Expand All @@ -80,42 +89,52 @@ def undo(self):


class UpdateCmdLineArgsCommand(SpineToolboxCommand):
def __init__(self, item, cmd_line_args):
def __init__(self, item_name, cmd_line_args, project):
"""Command to update Tool command line args.
Args:
item (ProjectItemBase): the item
item_name (str): item's name
cmd_line_args (list): list of command line args
project (SpineToolboxProject): project
"""
super().__init__()
self.item = item
self.redo_cmd_line_args = cmd_line_args
self.undo_cmd_line_args = self.item.cmd_line_args
self.setText(f"change command line arguments of {item.name}")
self._item_name = item_name
self._redo_cmd_line_args = cmd_line_args
item = project.get_item(item_name)
self._undo_cmd_line_args = item.cmd_line_args
self._project = project
self.setText(f"change command line arguments of {item_name}")

Check warning on line 106 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L101-L106

Added lines #L101 - L106 were not covered by tests

def redo(self):
self.item.update_cmd_line_args(self.redo_cmd_line_args)
item = self._project.get_item(self._item_name)
item.update_cmd_line_args(self._redo_cmd_line_args)

Check warning on line 110 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L109-L110

Added lines #L109 - L110 were not covered by tests

def undo(self):
self.item.update_cmd_line_args(self.undo_cmd_line_args)
item = self._project.get_item(self._item_name)
item.update_cmd_line_args(self._undo_cmd_line_args)

Check warning on line 114 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L113-L114

Added lines #L113 - L114 were not covered by tests


class UpdateGroupIdCommand(SpineToolboxCommand):
def __init__(self, item, group_id):
def __init__(self, item_name, group_id, project):
"""Command to update item group identifier.
Args:
item (ProjectItemBase): the item
item_name (str): item's name
group_id (str): group identifier
project (SpineToolboxProject): project
"""
super().__init__()
self._item = item
self._item_name = item_name

Check warning on line 127 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L127

Added line #L127 was not covered by tests
self._redo_group_id = group_id
self._undo_group_id = self._item.group_id
self.setText(f"change group identifier of {item.name}")
item = project.get_item(item_name)
self._undo_group_id = item.group_id
self._project = project
self.setText(f"change group identifier of {item_name}")

Check warning on line 132 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L129-L132

Added lines #L129 - L132 were not covered by tests

def redo(self):
self._item.do_set_group_id(self._redo_group_id)
item = self._project.get_item(self._item_name)
item.do_set_group_id(self._redo_group_id)

Check warning on line 136 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L135-L136

Added lines #L135 - L136 were not covered by tests

def undo(self):
self._item.do_set_group_id(self._undo_group_id)
item = self._project.get_item(self._item_name)
item.do_set_group_id(self._undo_group_id)

Check warning on line 140 in spine_items/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/commands.py#L139-L140

Added lines #L139 - L140 were not covered by tests
60 changes: 36 additions & 24 deletions spine_items/data_connection/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,67 +18,79 @@
class AddDCReferencesCommand(SpineToolboxCommand):
"""Command to add DC references."""

def __init__(self, dc, file_refs, db_refs):
def __init__(self, dc_name, file_refs, db_refs, project):
"""
Args:
dc (DataConnection): the DC
dc_name (str): DC name
file_refs (list of str): list of file refs to add
db_refs (list of str): list of db refs to add
project (SpineToolboxProject): project
"""
super().__init__()
self.dc = dc
self.file_refs = file_refs
self.db_refs = db_refs
self.setText(f"add references to {dc.name}")
self._dc_name = dc_name
self._file_refs = file_refs
self._db_refs = db_refs
self._project = project
self.setText(f"add references to {dc_name}")

def redo(self):
self.dc.do_add_references(self.file_refs, self.db_refs)
dc = self._project.get_item(self._dc_name)
dc.do_add_references(self._file_refs, self._db_refs)

def undo(self):
self.dc.do_remove_references(self.file_refs, self.db_refs)
dc = self._project.get_item(self._dc_name)
dc.do_remove_references(self._file_refs, self._db_refs)

Check warning on line 42 in spine_items/data_connection/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_connection/commands.py#L41-L42

Added lines #L41 - L42 were not covered by tests


class RemoveDCReferencesCommand(SpineToolboxCommand):
"""Command to remove DC references."""

def __init__(self, dc, file_refs, db_refs):
def __init__(self, dc_name, file_refs, db_refs, project):
"""
Args:
dc (DataConnection): the DC
dc_name (str): DC name
file_refs (list of str): list of file refs to remove
db_refs (list of str): list of db refs to remove
project (SpineToolboxProject): project
"""
super().__init__()
self.dc = dc
self.file_refs = file_refs
self.db_refs = db_refs
self.setText(f"remove references from {dc.name}")
self._dc_name = dc_name
self._file_refs = file_refs
self._db_refs = db_refs
self._project = project
self.setText(f"remove references from {dc_name}")

def redo(self):
self.dc.do_remove_references(self.file_refs, self.db_refs)
dc = self._project.get_item(self._dc_name)
dc.do_remove_references(self._file_refs, self._db_refs)

def undo(self):
self.dc.do_add_references(self.file_refs, self.db_refs)
dc = self._project.get_item(self._dc_name)
dc.do_add_references(self._file_refs, self._db_refs)

Check warning on line 69 in spine_items/data_connection/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_connection/commands.py#L68-L69

Added lines #L68 - L69 were not covered by tests


class MoveReferenceToData(SpineToolboxCommand):
"""Command to move DC references to data."""

def __init__(self, dc, paths):
def __init__(self, dc_name, paths, project):
"""
Args:
dc (DataConnection): the DC
dc_name (str): DC name
paths (list of str): list of paths to move
project (SpineToolboxProject): project
"""
super().__init__()
self._dc = dc
self._dc_name = dc_name
self._paths = paths
self.setText("copy references to data")
self._project = project
self.setText(f"copy references to data in {dc_name}")

def redo(self):
self._dc.do_copy_to_project(self._paths)
self._dc.do_remove_references(self._paths, [])
dc = self._project.get_item(self._dc_name)
dc.do_copy_to_project(self._paths)
dc.do_remove_references(self._paths, [])

def undo(self):
self._dc.delete_files_from_project([Path(p).name for p in self._paths])
self._dc.do_add_references(self._paths, [])
dc = self._project.get_item(self._dc_name)

Check warning on line 94 in spine_items/data_connection/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_connection/commands.py#L94

Added line #L94 was not covered by tests
dc.delete_files_from_project([Path(p).name for p in self._paths])
dc.do_add_references(self._paths, [])

Check warning on line 96 in spine_items/data_connection/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_connection/commands.py#L96

Added line #L96 was not covered by tests
12 changes: 8 additions & 4 deletions spine_items/data_connection/data_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def _add_file_references(self, paths):
if repeated_paths:
self._logger.msg_warning.emit(f"Reference to file(s) <b>{repeated_paths}</b> already exists")
if new_paths:
self._toolbox.undo_stack.push(AddDCReferencesCommand(self, new_paths, []))
self._toolbox.undo_stack.push(AddDCReferencesCommand(self.name, new_paths, [], self._project))

@Slot(bool)
def show_add_db_reference_dialog(self, _=False):
Expand All @@ -231,7 +231,7 @@ def show_add_db_reference_dialog(self, _=False):
self._database_validator.validate_url(
url["dialect"], sa_url, self._log_database_reference_error, success_slot=None
)
self._toolbox.undo_stack.push(AddDCReferencesCommand(self, [], [url]))
self._toolbox.undo_stack.push(AddDCReferencesCommand(self.name, [], [url], self._project))

def _has_db_reference(self, url):
"""Checks if given database URL exists already.
Expand Down Expand Up @@ -302,7 +302,9 @@ def remove_references(self, _=False):
file_references.append(index.data(Qt.ItemDataRole.DisplayRole))
elif parent == db_ref_root_index:
db_references.append(index.data(_Role.DB_URL_REFERENCE))
self._toolbox.undo_stack.push(RemoveDCReferencesCommand(self, file_references, db_references))
self._toolbox.undo_stack.push(
RemoveDCReferencesCommand(self.name, file_references, db_references, self._project)
)
self._logger.msg.emit("Selected references removed")

def do_remove_references(self, file_refs, db_refs):
Expand Down Expand Up @@ -496,7 +498,9 @@ def copy_to_project(self, _=False):
if not selected_indexes:
self._logger.msg_warning.emit("No files to copy")
return
self._toolbox.undo_stack.push(MoveReferenceToData(self, [index.data() for index in selected_indexes]))
self._toolbox.undo_stack.push(
MoveReferenceToData(self.name, [index.data() for index in selected_indexes], self._project)
)

def do_copy_to_project(self, paths):
"""Copies given files to item's data directory.
Expand Down
46 changes: 28 additions & 18 deletions spine_items/data_store/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,41 @@ class CommandId(IntEnum):


class UpdateDSURLCommand(SpineToolboxCommand):
def __init__(self, ds, was_url_valid, **kwargs):
"""Command to update DS url.
"""Command to update DS url."""

def __init__(self, ds_name, was_url_valid, project, **kwargs):
"""
Args:
ds (DataStore): the DS
ds_name (str): DS name
was_url_valid (bool): True if previous URL was valid, False otherwise
kwargs: url keys and their values
project (SpineToolboxProject): project
**kwargs: url keys and their values
"""
super().__init__()
self.ds = ds
self._ds_name = ds_name
self._undo_url_is_valid = was_url_valid
self.redo_kwargs = kwargs
self.undo_kwargs = {k: self.ds.url()[k] for k in kwargs}
self._redo_kwargs = kwargs
ds = project.get_item(ds_name)
self._undo_kwargs = {k: ds.url()[k] for k in kwargs}
self._project = project
if len(kwargs) == 1:
self.setText(f"change {list(kwargs.keys())[0]} of {ds.name}")
self.setText(f"change {list(kwargs.keys())[0]} of {ds_name}")
else:
self.setText(f"change url of {ds.name}")
self.setText(f"change url of {ds_name}")

def id(self):
return CommandId.UPDATE_URL.value

def mergeWith(self, command):
if not isinstance(command, UpdateDSURLCommand) or self.ds is not command.ds or command._undo_url_is_valid:
if (
not isinstance(command, UpdateDSURLCommand)
or self._ds_name != command._ds_name
or command._undo_url_is_valid
):
return False
diff_key = None
for key, value in self.redo_kwargs.items():
old_value = self.undo_kwargs[key]
for key, value in self._redo_kwargs.items():
old_value = self._undo_kwargs[key]

Check warning on line 58 in spine_items/data_store/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_store/commands.py#L58

Added line #L58 was not covered by tests
if value != old_value:
if diff_key is not None:
return False
Expand All @@ -57,16 +65,18 @@ def mergeWith(self, command):
raise RuntimeError("Logic error: nothing changes between undo and redo URLs.")
if diff_key == "dialect":
return False
changed_value = command.redo_kwargs[diff_key]
if self.redo_kwargs[diff_key] == changed_value:
changed_value = command._redo_kwargs[diff_key]

Check warning on line 68 in spine_items/data_store/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_store/commands.py#L68

Added line #L68 was not covered by tests
if self._redo_kwargs[diff_key] == changed_value:
return False
self.redo_kwargs[diff_key] = changed_value
if self.redo_kwargs[diff_key] == self.undo_kwargs[diff_key]:
self._redo_kwargs[diff_key] = changed_value

Check warning on line 71 in spine_items/data_store/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_store/commands.py#L71

Added line #L71 was not covered by tests
if self._redo_kwargs[diff_key] == self._undo_kwargs[diff_key]:
self.setObsolete(True)
return True

def redo(self):
self.ds.do_update_url(**self.redo_kwargs)
ds = self._project.get_item(self._ds_name)
ds.do_update_url(**self._redo_kwargs)

def undo(self):
self.ds.do_update_url(**self.undo_kwargs)
ds = self._project.get_item(self._ds_name)
ds.do_update_url(**self._undo_kwargs)

Check warning on line 82 in spine_items/data_store/commands.py

View check run for this annotation

Codecov / codecov/patch

spine_items/data_store/commands.py#L81-L82

Added lines #L81 - L82 were not covered by tests
2 changes: 1 addition & 1 deletion spine_items/data_store/data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def update_url(self, **kwargs):
kwargs = {k: v for k, v in kwargs.items() if v != self._url[k]}
if not kwargs:
return False
self._toolbox.undo_stack.push(UpdateDSURLCommand(self, invalidating_url, **kwargs))
self._toolbox.undo_stack.push(UpdateDSURLCommand(self.name, invalidating_url, self._project, **kwargs))
return True

def do_update_url(self, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def setupUi(self, Form):

def retranslateUi(self, Form):
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
self.specification_label.setText(QCoreApplication.translate("Form", u"Specification", None))
self.specification_label.setText(QCoreApplication.translate("Form", u"Specification:", None))
#if QT_CONFIG(tooltip)
self.specification_combo_box.setToolTip(QCoreApplication.translate("Form", u"<html><head/><body><p>Tool specification for this Tool</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<!--
######################################################################################################################
# Copyright (C) 2017-2022 Spine project consortium
# Copyright Spine Items contributors
# This file is part of Spine Items.
# Spine Items is free software: you can redistribute it and\/or modify it under the terms of the GNU Lesser General
# Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
Expand Down Expand Up @@ -46,7 +47,7 @@
</size>
</property>
<property name="text">
<string>Specification</string>
<string>Specification:</string>
</property>
</widget>
</item>
Expand Down
Loading

0 comments on commit 69a4673

Please sign in to comment.