fix(qt): auto-close Plugins manager, read-only field styling, RLock-safe heirs persistence
GUI / plugin lifecycle: - Auto-close Electrum's native 'Electrum Plugins' manager dialog after the BAL plugin is hot-enabled. Electrum 4.7.x no longer calls the old init_qt hook, so the close is now triggered from the create_status_bar, init_menubar and load_wallet hooks (fired when reload_windows() recreates the window). - Robust dialog matching (isinstance / class name / localized window title) to cope with zipimport module-identity mismatches. - Robust dismissal of the modal dialog (reject()/done()/close()) with a retry schedule [400, 800, 1500] ms; if it still cannot be closed, fall back to bringing it to the front (showNormal/raise_/activateWindow) so it never lingers hidden in the background. Counting only visible top-levels avoids treating an already-closed dialog as still open. Read-only field styling: - Paint the locked Delivery time / Check Alive date editors and the mining-fee spinbox with a light-grey background (#f0f0f0) so the user can see at a glance that they are not editable outside the 'Build your will' wizard; the styling is cleared when the fields are made editable again. Pickle/RLock crash on 'Build will': - heirs.save() now sanitises the heirs mapping via _json_safe() before handing it to json_db.put(), which deep-copies the value. A live runtime object (holding a threading.RLock) slipping into an heir value previously raised 'TypeError: cannot pickle _thread.RLock object' and aborted the task; such values are now coerced to str and logged with their path. - init_heirs_to_locktime() coerces the locktime to a plain serializable scalar. - log_error() now accepts both a sys.exc_info() triple and a single exception instance, fixing the secondary 'TypeError object is not subscriptable' that masked the real error.
This commit is contained in:
committed by
steal
parent
5b334b4193
commit
4e027d9e8b
@@ -64,6 +64,23 @@ class BalTxFeesWidget(QWidget):
|
||||
|
||||
def doubleclick(self, event=None):
|
||||
pass
|
||||
|
||||
def set_read_only(self, read_only=True):
|
||||
# Show the fee but make it non-editable (no spin arrows, no keyboard),
|
||||
# so it can only be changed from the "Build your will" wizard.
|
||||
self.txfee_widget.setReadOnly(read_only)
|
||||
self.txfee_widget.setButtonSymbols(
|
||||
QAbstractSpinBox.ButtonSymbols.NoButtons
|
||||
if read_only
|
||||
else QAbstractSpinBox.ButtonSymbols.UpDownArrows
|
||||
)
|
||||
# Light-grey background when locked, so the read-only state is visible
|
||||
# (same look as the date fields); empty stylesheet restores the
|
||||
# editable appearance used inside the wizard.
|
||||
self.txfee_widget.setStyleSheet(
|
||||
"QSpinBox{background-color:#f0f0f0;}" if read_only else ""
|
||||
)
|
||||
|
||||
def get_value(self):
|
||||
return self.txfee_widget.value()
|
||||
|
||||
@@ -275,6 +292,17 @@ class BalTimeEditWidget(QWidget, _LockTimeEditor):
|
||||
self.current_value = x
|
||||
self.bal_window.update_setting_widgets(x, self.base_field)
|
||||
|
||||
def set_read_only(self, read_only=True):
|
||||
"""Show the value but make it non-editable.
|
||||
|
||||
Used everywhere except the "Build your will" wizard, where the date is
|
||||
the only place the user is allowed to change it. The Raw/Date combo is
|
||||
disabled and both editors become read-only with no spin buttons.
|
||||
"""
|
||||
self.combo.setEnabled(not read_only)
|
||||
for w in self.editors:
|
||||
w.set_read_only(read_only)
|
||||
|
||||
|
||||
|
||||
class TimeRawEditWidget(QWidget):
|
||||
@@ -295,6 +323,14 @@ class TimeRawEditWidget(QWidget):
|
||||
self.get_value = self.editor.get_value
|
||||
self.set_value = self.editor.set_value
|
||||
|
||||
def set_read_only(self, read_only=True):
|
||||
self.editor.setReadOnly(read_only)
|
||||
# Match the Date editor: grey background when locked so the read-only
|
||||
# state is visible; empty stylesheet restores the editable look.
|
||||
self.editor.setStyleSheet(
|
||||
"QLineEdit{background-color:#f0f0f0;}" if read_only else ""
|
||||
)
|
||||
|
||||
|
||||
|
||||
class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
|
||||
@@ -387,6 +423,24 @@ class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
|
||||
#self.setDateTime(QDateTime.currentDateTime())
|
||||
self.time_edit = time_edit
|
||||
|
||||
def set_read_only(self, read_only=True):
|
||||
# Read-only display: keyboard editing disabled and the up/down spin
|
||||
# arrows removed, so the date can only be changed from the wizard.
|
||||
self.setReadOnly(read_only)
|
||||
self.setButtonSymbols(
|
||||
QAbstractSpinBox.ButtonSymbols.NoButtons
|
||||
if read_only
|
||||
else QAbstractSpinBox.ButtonSymbols.UpDownArrows
|
||||
)
|
||||
# A read-only QDateTimeEdit keeps a white background by default, which
|
||||
# does not visually signal that it is locked. Paint it light grey (like
|
||||
# the disabled combo/fee fields next to it) so the user sees at a glance
|
||||
# that the date is not editable here; an empty stylesheet restores the
|
||||
# default look when the field is made editable again (in the wizard).
|
||||
self.setStyleSheet(
|
||||
"QDateTimeEdit{background-color:#f0f0f0;}" if read_only else ""
|
||||
)
|
||||
|
||||
def get_value(self) -> Optional[int]:
|
||||
#dt = self.dateTime().toPyDateTime()
|
||||
#locktime = int(time.mktime(dt.timetuple()))
|
||||
@@ -419,11 +473,15 @@ class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
|
||||
|
||||
|
||||
class ThresholdTimeWidget(BalTimeEditWidget):
|
||||
# rich_text=True is used by the HelpButton, so HTML tags (<b>, <br>) render.
|
||||
help_text = (
|
||||
"Check to ask for invalidation.\n\n"
|
||||
"When less then this time is missing, ask to invalidate.\n"
|
||||
"If you fail to invalidate during this time, your transactions will be delivered to your heirs.\n\n"
|
||||
f"{BalTimeEditWidget.help_text}"
|
||||
"<b>CHECK ALIVE</b><br><br>"
|
||||
"Check to ask for invalidation.<br><br>"
|
||||
"When less then this time is missing, ask to invalidate.<br>"
|
||||
"If you fail to invalidate during this time, your transactions will be delivered to your heirs.<br><br>"
|
||||
"if you choose Raw, you can insert various options based on suffix:<br>"
|
||||
" - d: number of days after current day(ex: 1d means tomorrow)<br>"
|
||||
" - y: number of years after currrent day(ex: 1y means one year from today)<br>"
|
||||
)
|
||||
label_text = "🚨"
|
||||
#label_text = "Check Alive"
|
||||
@@ -441,10 +499,14 @@ class ThresholdTimeWidget(BalTimeEditWidget):
|
||||
|
||||
|
||||
class LockTimeWidget(BalTimeEditWidget):
|
||||
# rich_text=True is used by the HelpButton, so HTML tags (<b>, <br>) render.
|
||||
help_text = (
|
||||
"Set Locktime for transactions.\n"
|
||||
"Any time is needed transaction will be anticipated by 1day\n"
|
||||
f"{BalTimeEditWidget.help_text}"
|
||||
"<b>DELIVERY TIME</b><br><br>"
|
||||
"Set Locktime for transactions.<br>"
|
||||
"Any time is needed transaction will be anticipated by 1day<br><br>"
|
||||
"if you choose Raw, you can insert various options based on suffix:<br>"
|
||||
" - d: number of days after current day(ex: 1d means tomorrow)<br>"
|
||||
" - y: number of years after currrent day(ex: 1y means one year from today)<br>"
|
||||
)
|
||||
label_text = "🚛"
|
||||
#label_text = "Locktime"
|
||||
@@ -463,10 +525,15 @@ class LockTimeWidget(BalTimeEditWidget):
|
||||
|
||||
class WillSettingsWidget(QWidget):
|
||||
|
||||
def __init__(self, bal_window: "BalWindow", parent, layout_type="h"):
|
||||
def __init__(self, bal_window: "BalWindow", parent, layout_type="h",
|
||||
read_only=True):
|
||||
self.widgets = {}
|
||||
QWidget.__init__(self, parent)
|
||||
self.bal_window = bal_window
|
||||
# When read_only=True (toolbars, Heirs tab) the delivery time, check
|
||||
# alive and fee fields are display-only; they can only be edited from
|
||||
# the "Build your will" wizard, which passes read_only=False.
|
||||
self.read_only = read_only
|
||||
box = QHBoxLayout(self) if layout_type == "h" else QVBoxLayout(self)
|
||||
|
||||
self.calendar_button = QPushButton()
|
||||
@@ -496,6 +563,11 @@ class WillSettingsWidget(QWidget):
|
||||
box.addWidget(self.calendar_button)
|
||||
box.addWidget(self.widgets["baltx_fees"])
|
||||
|
||||
if self.read_only:
|
||||
self.widgets["locktime"].set_read_only(True)
|
||||
self.widgets["threshold"].set_read_only(True)
|
||||
self.widgets["baltx_fees"].set_read_only(True)
|
||||
|
||||
def create_alarms(self, alarm_start, alarm_end):
|
||||
days = (alarm_end - alarm_start).days+1
|
||||
lines = []
|
||||
|
||||
Reference in New Issue
Block a user