diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index a34b7f272..279c2ff1f 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -45,7 +45,7 @@ from PyQt5.QtWidgets import (QMessageBox, QComboBox, QSystemTrayIcon, QTabWidget QVBoxLayout, QGridLayout, QLineEdit, QHBoxLayout, QPushButton, QScrollArea, QTextEdit, QShortcut, QMainWindow, QCompleter, QInputDialog, - QWidget, QSizePolicy, QStatusBar, QToolTip) + QWidget, QSizePolicy, QStatusBar, QToolTip, QDialog) import electrum from electrum import (keystore, ecc, constants, util, bitcoin, commands, @@ -89,7 +89,7 @@ from .util import (read_QIcon, ColorScheme, text_dialog, icon_path, WaitingDialo CloseButton, HelpButton, MessageBoxMixin, EnterButton, import_meta_gui, export_meta_gui, filename_field, address_field, char_width_in_lineedit, webopen, - TRANSACTION_FILE_EXTENSION_FILTER, MONOSPACE_FONT) + TRANSACTION_FILE_EXTENSION_FILTER_ANY, MONOSPACE_FONT) from .util import ButtonsTextEdit from .installwizard import WIF_HELP_TEXT from .history_list import HistoryList, HistoryModel @@ -780,13 +780,27 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.config.set_key('io_dir', os.path.dirname(fileName), True) return fileName - def getSaveFileName(self, title, filename, filter = ""): + def getSaveFileName(self, title, filename, filter="", + *, default_extension: str = None, + default_filter: str = None) -> Optional[str]: directory = self.config.get('io_dir', os.path.expanduser('~')) - path = os.path.join( directory, filename ) - fileName, __ = QFileDialog.getSaveFileName(self, title, path, filter) - if fileName and directory != os.path.dirname(fileName): - self.config.set_key('io_dir', os.path.dirname(fileName), True) - return fileName + path = os.path.join(directory, filename) + + file_dialog = QFileDialog(self, title, path, filter) + file_dialog.setAcceptMode(QFileDialog.AcceptSave) + if default_extension: + # note: on MacOS, the selected filter's first extension seems to have priority over this... + file_dialog.setDefaultSuffix(default_extension) + if default_filter: + assert default_filter in filter, f"default_filter={default_filter!r} does not appear in filter={filter!r}" + file_dialog.selectNameFilter(default_filter) + if file_dialog.exec() != QDialog.Accepted: + return None + + selected_path = file_dialog.selectedFiles()[0] + if selected_path and directory != os.path.dirname(selected_path): + self.config.set_key('io_dir', os.path.dirname(selected_path), True) + return selected_path def timer_actions(self): self.request_list.refresh_status() @@ -2509,7 +2523,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): def read_tx_from_file(self) -> Optional[Transaction]: fileName = self.getOpenFileName(_("Select your transaction file"), - TRANSACTION_FILE_EXTENSION_FILTER) + TRANSACTION_FILE_EXTENSION_FILTER_ANY) if not fileName: return try: diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py index 8be43cf8f..97bd48dd0 100644 --- a/electrum/gui/qt/transaction_dialog.py +++ b/electrum/gui/qt/transaction_dialog.py @@ -50,7 +50,9 @@ from electrum.logging import get_logger from .util import (MessageBoxMixin, read_QIcon, Buttons, icon_path, MONOSPACE_FONT, ColorScheme, ButtonsLineEdit, text_dialog, - char_width_in_lineedit, TRANSACTION_FILE_EXTENSION_FILTER, + char_width_in_lineedit, TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE, + TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX, + TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX, BlockingWaitingDialog) from .fee_slider import FeeSlider @@ -323,12 +325,19 @@ class BaseTxDialog(QDialog, MessageBoxMixin): if isinstance(tx, PartialTransaction): tx.finalize_psbt() if tx.is_complete(): - name = 'signed_%s.txn' % (tx.txid()[0:8]) + name = 'signed_%s' % (tx.txid()[0:8]) + extension = 'txn' + default_filter = TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX else: - name = self.wallet.basename() + time.strftime('-%Y%m%d-%H%M.psbt') + name = self.wallet.basename() + time.strftime('-%Y%m%d-%H%M') + extension = 'psbt' + default_filter = TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX + name = f'{name}.{extension}' fileName = self.main_window.getSaveFileName(_("Select where to save your transaction"), name, - TRANSACTION_FILE_EXTENSION_FILTER) + TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE, + default_extension=extension, + default_filter=default_filter) if not fileName: return if tx.is_complete(): # network tx hex diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 2a2278a61..7e32b2103 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -51,7 +51,12 @@ pr_icons = { # filter tx files in QFileDialog: -TRANSACTION_FILE_EXTENSION_FILTER = "Transaction (*.txn *.psbt);;All files (*)" +TRANSACTION_FILE_EXTENSION_FILTER_ANY = "Transaction (*.txn *.psbt);;All files (*)" +TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX = "Partial Transaction (*.psbt)" +TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX = "Complete Transaction (*.txn)" +TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE = (f"{TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX};;" + f"{TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX};;" + f"All files (*)") class EnterButton(QPushButton):