From c098a8aa9138483f5521f7ad3e28369f7946ccf9 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 21:27:31 +0700
Subject: [PATCH 01/12] Add "Save as..." feature for dictionary conversion
---
plover/gui_qt/dictionaries_table.py | 12 ++++++++++++
plover/gui_qt/dictionaries_widget.py | 16 ++++++++++++++++
plover/gui_qt/dictionaries_widget.ui | 9 ++++++++-
3 files changed, 36 insertions(+), 1 deletion(-)
create mode 100644 plover/gui_qt/dictionaries_table.py
diff --git a/plover/gui_qt/dictionaries_table.py b/plover/gui_qt/dictionaries_table.py
new file mode 100644
index 000000000..adc527134
--- /dev/null
+++ b/plover/gui_qt/dictionaries_table.py
@@ -0,0 +1,12 @@
+from PyQt5.QtWidgets import QWidget, QMenu, QAction, QTableWidget
+
+class DictionariesTable(QTableWidget):
+ def contextMenuEvent(self, event: "QContextMenuEvent"):
+ menu = QMenu(self)
+ saveAsAction = QAction("Save as...", self)
+ row = self.rowAt(event.y())
+ assert row >= 0
+ saveAsAction.triggered.connect(lambda: self.parent().on_save_as(row))
+ menu.addAction(saveAsAction)
+ menu.popup(event.globalPos())
+
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 2408274e6..5ed22f243 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -377,6 +377,22 @@ def _create_new_dictionary(self):
self._update_dictionaries(dictionaries, keep_selection=False,
loaded_dictionaries=self._loaded_dictionaries)
+ def on_save_as(self, row: int) -> None:
+ new_filename = QFileDialog.getSaveFileName(
+ self, _('Save dictionary as'), None,
+ _dictionary_filters(include_readonly=False),
+ )[0]
+ if not new_filename:
+ return
+ new_filename = normalize_path(new_filename)
+ try:
+ d = create_dictionary(new_filename, threaded_save=False)
+ d.update(self._loaded_dictionaries[self._config_dictionaries[row].path])
+ d.save()
+ except:
+ log.error('creating dictionary %s failed', new_filename, exc_info=True)
+ return
+
def on_add_translation(self):
selection = self._get_selection()
if selection:
diff --git a/plover/gui_qt/dictionaries_widget.ui b/plover/gui_qt/dictionaries_widget.ui
index bdc028f91..341a4d326 100644
--- a/plover/gui_qt/dictionaries_widget.ui
+++ b/plover/gui_qt/dictionaries_widget.ui
@@ -33,7 +33,7 @@
0
-
-
+
QFrame::Box
@@ -173,6 +173,13 @@
+
+
+ DictionariesTable
+ QTableWidget
+ plover.gui_qt.dictionaries_table
+
+
From ad252b9c33e67388c8c793ad00874b01e33a3828 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 22:44:03 +0700
Subject: [PATCH 02/12] Remove type hints
---
plover/gui_qt/dictionaries_table.py | 2 +-
plover/gui_qt/dictionaries_widget.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/plover/gui_qt/dictionaries_table.py b/plover/gui_qt/dictionaries_table.py
index adc527134..f0a3b37b7 100644
--- a/plover/gui_qt/dictionaries_table.py
+++ b/plover/gui_qt/dictionaries_table.py
@@ -1,7 +1,7 @@
from PyQt5.QtWidgets import QWidget, QMenu, QAction, QTableWidget
class DictionariesTable(QTableWidget):
- def contextMenuEvent(self, event: "QContextMenuEvent"):
+ def contextMenuEvent(self, event):
menu = QMenu(self)
saveAsAction = QAction("Save as...", self)
row = self.rowAt(event.y())
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 5ed22f243..0e63b7d6a 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -377,7 +377,7 @@ def _create_new_dictionary(self):
self._update_dictionaries(dictionaries, keep_selection=False,
loaded_dictionaries=self._loaded_dictionaries)
- def on_save_as(self, row: int) -> None:
+ def on_save_as(self, row):
new_filename = QFileDialog.getSaveFileName(
self, _('Save dictionary as'), None,
_dictionary_filters(include_readonly=False),
From 3791aa76c0b69b44c457d965cb1f755148343b95 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 22:46:01 +0700
Subject: [PATCH 03/12] Reword context menu entry name
---
plover/gui_qt/dictionaries_table.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plover/gui_qt/dictionaries_table.py b/plover/gui_qt/dictionaries_table.py
index f0a3b37b7..4042039b9 100644
--- a/plover/gui_qt/dictionaries_table.py
+++ b/plover/gui_qt/dictionaries_table.py
@@ -3,7 +3,7 @@
class DictionariesTable(QTableWidget):
def contextMenuEvent(self, event):
menu = QMenu(self)
- saveAsAction = QAction("Save as...", self)
+ saveAsAction = QAction("Save a Copy As...", self)
row = self.rowAt(event.y())
assert row >= 0
saveAsAction.triggered.connect(lambda: self.parent().on_save_as(row))
From 39368759a72c23581246d653ee1624d5a29a18d3 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 22:53:04 +0700
Subject: [PATCH 04/12] Set default file name of dictionary save-as feature
---
plover/gui_qt/dictionaries_widget.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 0e63b7d6a..040d53207 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -378,8 +378,9 @@ def _create_new_dictionary(self):
loaded_dictionaries=self._loaded_dictionaries)
def on_save_as(self, row):
+ dictionary_path = self._config_dictionaries[row].path
new_filename = QFileDialog.getSaveFileName(
- self, _('Save dictionary as'), None,
+ self, _('Save dictionary as'), dictionary_path,
_dictionary_filters(include_readonly=False),
)[0]
if not new_filename:
@@ -387,7 +388,7 @@ def on_save_as(self, row):
new_filename = normalize_path(new_filename)
try:
d = create_dictionary(new_filename, threaded_save=False)
- d.update(self._loaded_dictionaries[self._config_dictionaries[row].path])
+ d.update(self._loaded_dictionaries[dictionary_path])
d.save()
except:
log.error('creating dictionary %s failed', new_filename, exc_info=True)
From d42f5310e95a7692e764883348d8b30377ddf157 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 22:54:09 +0700
Subject: [PATCH 05/12] Fix save-a-copy-as dialog box title accordingly
---
plover/gui_qt/dictionaries_widget.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 040d53207..bfa756696 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -380,7 +380,7 @@ def _create_new_dictionary(self):
def on_save_as(self, row):
dictionary_path = self._config_dictionaries[row].path
new_filename = QFileDialog.getSaveFileName(
- self, _('Save dictionary as'), dictionary_path,
+ self, _('Save a Copy As...'), dictionary_path,
_dictionary_filters(include_readonly=False),
)[0]
if not new_filename:
From db99fbf70f54af7387964d1fd54c109c1132fab7 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Fri, 16 Oct 2020 23:08:29 +0700
Subject: [PATCH 06/12] Disable save a copy as action if there are more than
one rows selected
---
plover/gui_qt/dictionaries_table.py | 10 ++--------
plover/gui_qt/dictionaries_widget.py | 16 ++++++++++++++++
2 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/plover/gui_qt/dictionaries_table.py b/plover/gui_qt/dictionaries_table.py
index 4042039b9..b7e8301c3 100644
--- a/plover/gui_qt/dictionaries_table.py
+++ b/plover/gui_qt/dictionaries_table.py
@@ -1,12 +1,6 @@
-from PyQt5.QtWidgets import QWidget, QMenu, QAction, QTableWidget
+from PyQt5.QtWidgets import QTableWidget
class DictionariesTable(QTableWidget):
def contextMenuEvent(self, event):
- menu = QMenu(self)
- saveAsAction = QAction("Save a Copy As...", self)
row = self.rowAt(event.y())
- assert row >= 0
- saveAsAction.triggered.connect(lambda: self.parent().on_save_as(row))
- menu.addAction(saveAsAction)
- menu.popup(event.globalPos())
-
+ self.parent().on_table_context_menu(row, event.globalPos())
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index bfa756696..be8e5102a 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -15,6 +15,7 @@
QMenu,
QTableWidgetItem,
QWidget,
+ QAction,
)
from plover.config import DictionaryConfig
@@ -377,6 +378,21 @@ def _create_new_dictionary(self):
self._update_dictionaries(dictionaries, keep_selection=False,
loaded_dictionaries=self._loaded_dictionaries)
+ def on_table_context_menu(self, row, global_pos):
+ if row == -1:
+ # when the user right-clicks in the empty area of the table
+ return
+
+ menu = QMenu(self)
+ saveAsAction = QAction("Save a Copy As...", self)
+ saveAsAction.triggered.connect(lambda: self.on_save_as(row))
+
+ selected_rows = self._get_selection()
+ assert row in selected_rows
+ saveAsAction.setDisabled(len(selected_rows) != 1)
+ menu.addAction(saveAsAction)
+ menu.popup(global_pos)
+
def on_save_as(self, row):
dictionary_path = self._config_dictionaries[row].path
new_filename = QFileDialog.getSaveFileName(
From aad67282a7c24c62eab8c50e7bef4fa30b556a97 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Sat, 17 Oct 2020 08:18:04 +0700
Subject: [PATCH 07/12] Apply suggested changes (single quote, i18n)
---
plover/gui_qt/dictionaries_widget.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index be8e5102a..6f07b4acd 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -384,7 +384,7 @@ def on_table_context_menu(self, row, global_pos):
return
menu = QMenu(self)
- saveAsAction = QAction("Save a Copy As...", self)
+ saveAsAction = QAction(_('Save a Copy As...'), self)
saveAsAction.triggered.connect(lambda: self.on_save_as(row))
selected_rows = self._get_selection()
From 188131b4153b336ed6d36a3d19af78e9a538d013 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Sat, 17 Oct 2020 10:34:58 +0700
Subject: [PATCH 08/12] Reload dictionaries on dictionary save-a-copy
---
plover/gui_qt/dictionaries_widget.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 6f07b4acd..458d68ca5 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -409,6 +409,10 @@ def on_save_as(self, row):
except:
log.error('creating dictionary %s failed', new_filename, exc_info=True)
return
+ # Note: pass in `loaded_dictionaries` to force update (use case:
+ # the user decided to overwrite an already loaded dictionary).
+ self._update_dictionaries(self._config_dictionaries,
+ loaded_dictionaries=self._loaded_dictionaries)
def on_add_translation(self):
selection = self._get_selection()
From a09eb02856b846d75ab05c1341442b4c14716adc Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Sat, 17 Oct 2020 11:42:49 +0700
Subject: [PATCH 09/12] Fix memory leak, keep a reference to the row to save
instead of the row index
---
plover/gui_qt/dictionaries_widget.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 458d68ca5..80bb01f20 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -383,9 +383,17 @@ def on_table_context_menu(self, row, global_pos):
# when the user right-clicks in the empty area of the table
return
+ dictionary_path = self._config_dictionaries[row].path
menu = QMenu(self)
saveAsAction = QAction(_('Save a Copy As...'), self)
- saveAsAction.triggered.connect(lambda: self.on_save_as(row))
+ saveAsAction.triggered.connect(lambda: self.on_save_as(
+ default_name=dictionary_path,
+ dictionary=self._loaded_dictionaries[dictionary_path]))
+
+ def cleanup():
+ menu.deleteLater()
+ saveAsAction.deleteLater()
+ menu.aboutToHide.connect(cleanup)
selected_rows = self._get_selection()
assert row in selected_rows
@@ -393,10 +401,9 @@ def on_table_context_menu(self, row, global_pos):
menu.addAction(saveAsAction)
menu.popup(global_pos)
- def on_save_as(self, row):
- dictionary_path = self._config_dictionaries[row].path
+ def on_save_as(self, default_name, dictionary):
new_filename = QFileDialog.getSaveFileName(
- self, _('Save a Copy As...'), dictionary_path,
+ self, _('Save a Copy As...'), default_name,
_dictionary_filters(include_readonly=False),
)[0]
if not new_filename:
@@ -404,7 +411,7 @@ def on_save_as(self, row):
new_filename = normalize_path(new_filename)
try:
d = create_dictionary(new_filename, threaded_save=False)
- d.update(self._loaded_dictionaries[dictionary_path])
+ d.update(dictionary)
d.save()
except:
log.error('creating dictionary %s failed', new_filename, exc_info=True)
From dc2903f918d0004c524e1f7ff94af91861d001f3 Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Tue, 20 Oct 2020 14:50:17 +0700
Subject: [PATCH 10/12] Add towncrier snippet
---
news.d/feature/1149.ui.rst | 1 +
1 file changed, 1 insertion(+)
create mode 100644 news.d/feature/1149.ui.rst
diff --git a/news.d/feature/1149.ui.rst b/news.d/feature/1149.ui.rst
new file mode 100644
index 000000000..bb60ad0c0
--- /dev/null
+++ b/news.d/feature/1149.ui.rst
@@ -0,0 +1 @@
+Add "Save a Copy As..." feature to export dictionary to a different format.
From ed5a0521778922b3fb7cc32ad4a2357640b6f43e Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Sun, 4 Apr 2021 13:55:14 +0700
Subject: [PATCH 11/12] Convert news snippet to Markdown
---
news.d/feature/{1149.ui.rst => 1149.ui.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename news.d/feature/{1149.ui.rst => 1149.ui.md} (100%)
diff --git a/news.d/feature/1149.ui.rst b/news.d/feature/1149.ui.md
similarity index 100%
rename from news.d/feature/1149.ui.rst
rename to news.d/feature/1149.ui.md
From 6143b1fcf66fc6e0db9756ffd62935fc76b63fef Mon Sep 17 00:00:00 2001
From: user202729 <25191436+user202729@users.noreply.github.com>
Date: Sun, 4 Apr 2021 14:26:09 +0700
Subject: [PATCH 12/12] Implement 'Merge and Save a Copy As' feature
---
plover/gui_qt/dictionaries_widget.py | 30 ++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 80bb01f20..b103df840 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -383,25 +383,38 @@ def on_table_context_menu(self, row, global_pos):
# when the user right-clicks in the empty area of the table
return
- dictionary_path = self._config_dictionaries[row].path
+ selected_rows = self._get_selection()
+ assert list(selected_rows) == sorted(selected_rows)
+ if row not in selected_rows:
+ # in some cases (Ctrl+right click, for example), the current row might not be selected
+ return
+
menu = QMenu(self)
- saveAsAction = QAction(_('Save a Copy As...'), self)
+
+ # the path of the dictionary on the row the mouse clicked on
+ dictionary_path = self._config_dictionaries[row].path
+
+ if len(selected_rows) == 1:
+ saveAsAction = QAction(_('Save a Copy As...'), self)
+ else:
+ saveAsAction = QAction(_('Merge and Save a Copy As...'), self)
+
saveAsAction.triggered.connect(lambda: self.on_save_as(
default_name=dictionary_path,
- dictionary=self._loaded_dictionaries[dictionary_path]))
+ dictionaries=[
+ self._loaded_dictionaries[self._config_dictionaries[row].path]
+ for row in selected_rows]
+ ))
def cleanup():
menu.deleteLater()
saveAsAction.deleteLater()
menu.aboutToHide.connect(cleanup)
- selected_rows = self._get_selection()
- assert row in selected_rows
- saveAsAction.setDisabled(len(selected_rows) != 1)
menu.addAction(saveAsAction)
menu.popup(global_pos)
- def on_save_as(self, default_name, dictionary):
+ def on_save_as(self, default_name, dictionaries):
new_filename = QFileDialog.getSaveFileName(
self, _('Save a Copy As...'), default_name,
_dictionary_filters(include_readonly=False),
@@ -411,7 +424,8 @@ def on_save_as(self, default_name, dictionary):
new_filename = normalize_path(new_filename)
try:
d = create_dictionary(new_filename, threaded_save=False)
- d.update(dictionary)
+ for dictionary in reversed(dictionaries):
+ d.update(dictionary)
d.save()
except:
log.error('creating dictionary %s failed', new_filename, exc_info=True)