Merge pull request #10573 from f321x/qml_wallet_rename

qml: allow renaming wallets
This commit is contained in:
ThomasV
2026-04-22 10:03:14 +02:00
committed by GitHub
3 changed files with 106 additions and 0 deletions
+11
View File
@@ -585,6 +585,17 @@ class Daemon(Logger):
return True return True
return False return False
def rename_wallet_file(self, old_path: str, new_path: str):
old_path = standardize_path(old_path)
new_path = standardize_path(new_path)
if os.path.exists(new_path):
raise ValueError("Wallet file already exists")
os.rename(old_path, new_path)
self.logger.debug(f'renamed wallet: {old_path} -> {new_path}')
self.update_recently_opened_wallets(old_path, remove=True)
if self.config.CURRENT_WALLET == old_path:
self.config.CURRENT_WALLET = new_path
def stop_wallet(self, path: str) -> bool: def stop_wallet(self, path: str) -> bool:
"""Returns True iff a wallet was found.""" """Returns True iff a wallet was found."""
assert util.get_running_loop() != util.get_asyncio_loop(), 'must not be called from asyncio thread' assert util.get_running_loop() != util.get_asyncio_loop(), 'must not be called from asyncio thread'
@@ -130,6 +130,64 @@ Pane {
visible: Daemon.currentWallet visible: Daemon.currentWallet
columns: 2 columns: 2
Label {
Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall
text: qsTr('Name')
color: Material.accentColor
}
TextHighlightPane {
id: walletNameContent
property bool editmode: false
Layout.columnSpan: 2
Layout.fillWidth: true
RowLayout {
width: parent.width
Label {
visible: !walletNameContent.editmode
text: Daemon.currentWallet.name
wrapMode: Text.Wrap
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
}
ToolButton {
visible: !walletNameContent.editmode
icon.source: '../../icons/pen.png'
icon.color: 'transparent'
onClicked: {
walletNameEdit.text = Daemon.currentWallet.name
walletNameContent.editmode = true
walletNameEdit.focus = true
}
}
TextField {
id: walletNameEdit
visible: walletNameContent.editmode
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
}
ToolButton {
visible: walletNameContent.editmode
icon.source: '../../icons/confirmed.png'
icon.color: enabled ? 'transparent' : constants.mutedForeground
enabled: walletNameEdit.text !== Daemon.currentWallet.name
&& Daemon.isValidWalletName(walletNameEdit.text)
onClicked: {
walletNameContent.editmode = false
Daemon.renameWallet(walletNameEdit.text)
}
}
ToolButton {
visible: walletNameContent.editmode
icon.source: '../../icons/closebutton.png'
icon.color: 'transparent'
onClicked: walletNameContent.editmode = false
}
}
}
Label { Label {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall Layout.topMargin: constants.paddingSmall
@@ -526,6 +584,14 @@ Pane {
dialog.open() dialog.open()
} }
} }
function onWalletRenameError(message) {
var dialog = app.messageDialog.createObject(app, {
title: qsTr('Error'),
iconSource: Qt.resolvedUrl('../../icons/warning.png'),
text: message
})
dialog.open()
}
} }
Connections { Connections {
+29
View File
@@ -150,6 +150,7 @@ class QEDaemon(AuthMixin, QObject):
walletRequiresPassword = pyqtSignal([str, str], arguments=['name', 'path']) walletRequiresPassword = pyqtSignal([str, str], arguments=['name', 'path'])
walletOpenError = pyqtSignal([str], arguments=["error"]) walletOpenError = pyqtSignal([str], arguments=["error"])
walletDeleteError = pyqtSignal([str, str], arguments=['code', 'message']) walletDeleteError = pyqtSignal([str, str], arguments=['code', 'message'])
walletRenameError = pyqtSignal([str], arguments=['message'])
def __init__(self, daemon: 'Daemon', plugins: 'Plugins', parent=None): def __init__(self, daemon: 'Daemon', plugins: 'Plugins', parent=None):
super().__init__(parent) super().__init__(parent)
@@ -310,6 +311,34 @@ class QEDaemon(AuthMixin, QObject):
self.availableWallets.remove_wallet(path) self.availableWallets.remove_wallet(path)
@pyqtSlot(str, result=bool)
def isValidWalletName(self, wallet_name: str) -> bool:
if not wallet_name:
return False
if self.availableWallets.wallet_name_exists(wallet_name):
return False
# ensure wallet_name is not interpreted as path
if os.path.basename(wallet_name) != wallet_name: # '/foo/bar/' returns 'bar'
return False
return True
@pyqtSlot(str)
def renameWallet(self, new_name: str):
wallet = self._current_wallet
assert wallet, "name change without wallet?"
old_path = standardize_path(wallet.wallet.storage.path)
wallet_dir = os.path.dirname(old_path)
new_path = standardize_path(os.path.join(wallet_dir, new_name))
if old_path == new_path:
return
self._current_wallet = None
self.daemon.stop_wallet(old_path)
try:
self.daemon.rename_wallet_file(old_path, new_path)
except Exception as e:
self.walletRenameError.emit(_('Error renaming wallet:\n') + str(e))
self.walletLoaded.emit(None, None)
@pyqtProperty(bool, notify=loadingChanged) @pyqtProperty(bool, notify=loadingChanged)
def loading(self): def loading(self):
return self._loading return self._loading