diff --git a/VERSION b/VERSION
index a45be46..13dead7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.2.8
+0.2.10
diff --git a/manifest.json b/manifest.json
index 5edff73..f193e49 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"name": "BAL",
"fullname": "Bitcoin After Life",
- "description": "Provides free and decentralized inheritance support
Version: 0.2.7",
+ "description": "Provides free and decentralized inheritance support
Version: 0.2.10",
"author":"Svatantrya",
"available_for": ["qt"],
"icon":"icons/bal32x32.png"
diff --git a/qt.py b/qt.py
index 86e1b35..55a23f2 100644
--- a/qt.py
+++ b/qt.py
@@ -390,6 +390,26 @@ class shown_cv:
class BalWindow(Logger):
+ """Main application window for Bitcoin After Life inheritance management.
+
+ This class provides the primary UI for:
+ - Managing inheritance plans
+ - Building Wills
+ - Handling heir configurations
+ - Network operations
+ - Transaction signing and broadcasting
+
+ Attributes:
+ bal_plugin (BalPlugin): Reference to the plugin instance
+ config (dict): Application configuration
+ network (Network): Network interface
+ wallet (Abstract_Wallet): Wallet interface
+ heirs (list): List of heir configurations
+ will (Will): Current Will being managed
+ building_will (bool): Flag indicating Will construction in progress
+ stop_build (bool): Flag to stop Will construction
+ """
+
def __init__(self, bal_plugin: "BalPlugin", window: "ElectrumWindow"):
Logger.__init__(self)
self.bal_plugin = bal_plugin
@@ -1272,6 +1292,17 @@ class _LockTimeEditor:
class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
+ """HeirsLockTimeEdit class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = HeirsLockTimeEdit(required parameters)
+ """
+
valueEdited = pyqtSignal()
locktime_threshold = 50000000
@@ -1334,6 +1365,17 @@ class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
self.editor.set_locktime(x, force)
+ """LockTimeRawEdit class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = LockTimeRawEdit(required parameters)
+ """
+
class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
def __init__(self, parent=None, time_edit=None):
QLineEdit.__init__(self, parent)
@@ -1418,6 +1460,17 @@ class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
out = min(out, self.max_allowed_value)
self.setText(str(out))
+ """LockTimeHeightEdit class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = LockTimeHeightEdit(required parameters)
+ """
+
class LockTimeHeightEdit(LockTimeRawEdit):
max_allowed_value = NLOCKTIME_BLOCKHEIGHT_MAX
@@ -1448,6 +1501,17 @@ def get_max_allowed_timestamp() -> int:
ts = 2**31 - 1 # INT32_MAX
datetime.fromtimestamp(ts) # test if raises
return ts
+ """LockTimeDateEdit class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = LockTimeDateEdit(required parameters)
+ """
+
class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
@@ -1479,6 +1543,25 @@ class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
self.setDateTime(dt)
+ """Widget for editing percentage amounts.
+
+ Extends BTCAmountEdit to provide:
+ - Percentage-based input (0-100%)
+ - Validation of percentage values
+ - Conversion between percentage and satoshis
+ - Formatting for display
+
+ Attributes:
+ min_value (float): Minimum percentage (default 0.0)
+ max_value (float): Maximum percentage (default 100.0)
+ step (float): Increment step for percentage values
+
+ Methods:
+ value(): Return current percentage as float
+ setValue(): Set percentage value
+ validate(): Validate percentage range
+ """
+
_NOT_GIVEN = object() # sentinel value
@@ -1545,6 +1628,35 @@ class PercAmountEdit(BTCAmountEdit):
painter.drawText(
textRect,
int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter),
+ """Base class for dialog windows in Bitcoin After Life.
+
+ Provides common functionality for all dialog windows including:
+ - Window modal behavior
+ - Standard button layouts (OK, Cancel)
+ - Size policy management
+ - Parent window handling
+
+ Subclasses should implement:
+ - __init__: Initialize dialog components
+ - setup_ui: Create and configure UI elements
+ - connect_signals: Connect signal-slot connections
+
+ Attributes:
+ parent (QWidget): Parent window
+ plugin (BalPlugin): Plugin reference
+ title (str): Dialog window title
+
+ Methods:
+ exec_(): Show dialog modally
+ accept(): Handle dialog acceptance
+ reject(): Handle dialog rejection
+
+ Example:
+ dialog = BalDialog(parent, plugin, "My Dialog")
+ if dialog.exec_() == QDialog.Accepted:
+ # Handle acceptance
+ """
+
self.base_unit() + " or perc value",
)
@@ -1552,6 +1664,33 @@ class PercAmountEdit(BTCAmountEdit):
class BalDialog(WindowModalDialog):
def __init__(self, parent, bal_plugin, title=None, icon="icons/bal32x32.png"):
self.parent = parent
+ """Base class for multi-step wizard dialogs.
+
+ Provides infrastructure for:
+ - Multi-page workflows
+ - Navigation between steps
+ - Progress tracking
+ - Validation at each step
+ - Final submission
+
+ Subclasses implement specific wizards:
+ - BalWizardHeirsWidget: Heir configuration
+ - BalWizardWEDownloadWidget: Wallet export download
+ - BalWizardWEWidget: Wallet export configuration
+ - BalWizardLocktimeAndFeeWidget: Locktime and fee setup
+
+ Attributes:
+ current_page (int): Current step index
+ total_pages (int): Total number of steps
+ data (dict): Collected data across steps
+
+ Methods:
+ next_page(): Move to next step
+ prev_page(): Move to previous step
+ validate_current(): Validate current step
+ finish(): Complete the wizard
+ """
+
WindowModalDialog.__init__(self, parent, title)
# WindowModalDialog.__init__(self,parent)
self.setWindowIcon(read_QIcon_from_bytes(bal_plugin.read_file(icon)))
@@ -1640,6 +1779,17 @@ class BalWizardDialog(BalDialog):
self.bal_window.update_all()
pass
+ """BalWizardWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalWizardWidget(required parameters)
+ """
+
def closeEvent(self, event):
self.bal_window.heir_list_widget.update_will_settings()
@@ -1705,6 +1855,17 @@ class BalWizardWidget(QWidget):
def _on_previous(self):
self.on_previous()
+ """BalWizardHeirsWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalWizardHeirsWidget(required parameters)
+ """
+
def get_content(self):
pass
@@ -1739,6 +1900,17 @@ class BalWizardHeirsWidget(BalWizardWidget):
def export_to_file(self):
self.bal_window.export_heirs()
+ """BalWizardWEDownloadWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalWizardWEDownloadWidget(required parameters)
+ """
+
def add_heir(self):
self.bal_window.new_heir_dialog()
self.heir_list_widget.update()
@@ -1826,6 +1998,17 @@ class BalWizardWEDownloadWidget(BalWizardWidget):
if self.validate():
return self.on_next()
+ """BalWizardWEWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalWizardWEWidget(required parameters)
+ """
+
def import_json_file(self, path):
data = read_json_file(path)
data = self._validate(data)
@@ -1842,6 +2025,17 @@ class BalWizardWEWidget(BalWizardWidget):
def get_content(self):
widget = QWidget()
vbox = QVBoxLayout(widget)
+ """BalWizardLocktimeAndFeeWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalWizardLocktimeAndFeeWidget(required parameters)
+ """
+
vbox.addWidget(
WillExecutorWidget(
self,
@@ -1951,6 +2145,30 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
_("Fees(sats/vbyte):"),
self.heir_tx_fees,
("Fee to be used in the transaction"),
+ """Non-blocking progress dialog for long operations.
+
+ Provides:
+ - Indeterminate progress bar
+ - Status messages
+ - Cancel button for interruption
+ - Automatic cleanup
+ - Thread-safe operation
+
+ Used for operations like:
+ - Blockchain synchronization
+ - Transaction broadcasting
+ - Will construction
+ - Data loading
+
+ Attributes:
+ message (str): Current status message
+ can_cancel (bool): Whether operation can be cancelled
+
+ Methods:
+ update_message(): Update status message
+ cancel(): Interrupt the operation
+ """
+
)
)
@@ -2015,6 +2233,17 @@ class BalWaitingDialog(BalDialog):
self.thread.stop()
def update_message(self, msg):
+ """BalBlockingWaitingDialog class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = BalBlockingWaitingDialog(required parameters)
+ """
+
self.message_label.setText(msg)
def update(self, msg):
@@ -2034,6 +2263,17 @@ class BalBlockingWaitingDialog(BalDialog):
vbox = QVBoxLayout(self)
vbox.addWidget(self.message_label)
self.finished.connect(self.deleteLater) # see #3956
+ """bal_checkbox class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = bal_checkbox(required parameters)
+ """
+
# show popup
self.show()
# refresh GUI; needed for popup to appear and for message_label to get drawn
@@ -2069,6 +2309,18 @@ class BalBuildWillDialog(BalDialog):
COLOR_OK = "#05ad05"
def __init__(self, bal_window, parent=None):
+ """Initialize the Build Will dialog.
+
+ Args:
+ bal_window (BalWindow): The main application window
+ parent (QWidget, optional): Parent widget. Defaults to None.
+
+ Initializes:
+ - Main UI components (message label, container widget)
+ - Message queue system with debounce timer
+ - Layout management
+ - Network connection
+ """
if not parent:
parent = bal_window.window
BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will"))
@@ -2076,16 +2328,43 @@ class BalBuildWillDialog(BalDialog):
self.updatemessage.connect(self.msg_update)
self.bal_window = bal_window
self.bal_plugin = bal_window.bal_plugin
+
+ # Main message label
self.message_label = QLabel(_("Building Will:"))
self.vbox = QVBoxLayout(self)
- self.vbox.addWidget(self.message_label,0)
+ self.vbox.addWidget(self.message_label, 0)
+
+ # Container for dynamic messages
self.qwidget = QWidget(self)
- self.vbox.addWidget(self.qwidget,1)
- self.labelsbox=QVBoxLayout(self.qwidget)
+ self.vbox.addWidget(self.qwidget, 1)
+
+ # Layout for messages with reduced spacing
+ self.labelsbox = QVBoxLayout(self.qwidget)
+ self.labelsbox.setContentsMargins(0, 0, 0, 0)
+ self.labelsbox.setSpacing(4) # Reduced spacing between messages
+
+ # Set minimum dimensions
self.setMinimumWidth(600)
self.setMinimumHeight(100)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
- self.labels = []
+
+ # Message queue implementation for efficient updates
+ self._message_queue = [] # Thread-safe message queue
+ self._message_timer = QTimer(self)
+ self._message_timer.setSingleShot(True)
+ self._message_timer.setInterval(50) # Debounce interval: 50ms
+ self._message_timer.timeout.connect(self._process_message_queue)
+
+ # Other initialization
+ self.labels = [] # Immediate message storage
+ self.check_row = None
+ self.inval_row = None
+ self.build_row = None
+ self.sign_row = None
+ self.push_row = None
+ self.network = Network.get_instance()
+ self._stopping = False
+
self.check_row = None
self.inval_row = None
self.build_row = None
@@ -2535,13 +2814,103 @@ class BalBuildWillDialog(BalDialog):
w.deleteLater()
def msg_update(self):
+ """Updates the UI with new messages using a debounced queue system.
+
+ This method implements the following logic:
+ 1. Adds all pending messages to the queue
+ 2. Clears the immediate message storage
+ 3. Starts the debounce timer if not already active
+
+ The actual UI update happens in _process_message_queue after the
+ debounce interval to prevent excessive UI updates.
+
+ Note:
+ Thread-safe operation - can be called from any thread
+ """
+ self._message_queue.extend(self.labels)
+ self.labels = [] # Clear immediate labels after queuing
+ if not self._message_timer.isActive():
+ self._message_timer.start()
+
+ def _process_message_queue(self):
+ """Processes queued messages with debounce for efficient UI updates.
+
+ This method:
+ 1. Clears the existing layout
+ 2. Processes all queued messages
+ 3. Updates the UI once with all new messages
+ 4. Resets the queue
+ 5. Adjusts dialog height based on content
+
+ The debounce interval (50ms) ensures rapid message bursts are
+ processed in a single batch, reducing UI flicker.
+
+ Note:
+ Called automatically by QTimer after debounce interval
+ """
+ if not self._message_queue:
+ return
+
+ # Clear existing layout
self.clear_layout(self.labelsbox)
- for label in self.labels:
- label=label.replace("\n","
")
- qlabel=QLabel(label)
- self.labelsbox.addWidget(QLabel(label),1)
- self.setMinimumHeight(30*(len(self.labels)+2))
-
+
+ # Process all queued messages
+ for text in self._message_queue:
+ try:
+ # Format text for rich display
+ formatted_text = text.replace("\n", "
")
+
+ # Create label with proper settings
+ label = QLabel(formatted_text)
+ label.setWordWrap(True)
+ label.setTextFormat(Qt.TextFormat.RichText)
+ label.setOpenExternalLinks(False) # Security
+
+ # Set size policy
+ label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
+
+ # Add to layout
+ self.labelsbox.addWidget(label)
+
+ except Exception as e:
+ # Log errors without interrupting processing
+ """Table widget for managing heir configurations.
+
+ Provides a tabular interface for:
+ - Adding, editing, and removing heirs
+ - Configuring inheritance percentages
+ - Setting heir addresses and conditions
+ - Validating heir configurations
+ - Sorting and filtering heir list
+
+ Inherits from MyTreeView for:
+ - Column-based data display
+ - Sorting functionality
+ - Filtering capabilities
+ - Model-view architecture
+
+ Attributes:
+ bal_window (BalWindow): Reference to main window
+ main_window (QMainWindow): Parent window
+ model (QStandardItemModel): Data model for heirs
+
+ Columns:
+ NAME: Heir name
+ ADDRESS: Bitcoin address
+ AMOUNT: Inheritance percentage
+
+ Example:
+ widget = HeirListWidget(window, parent)
+ widget.update() # Refresh heir list
+ """
+
+ import logging
+ logging.error(f"Error creating label in BalBuildWillDialog: {e}")
+
+ # Reset queue and update dimensions
+ self._message_queue = []
+ self.setMinimumHeight(min(30 * (len(self.labels) + 2), 400)) # Max height limit
+
def get_text(self):
return self.message_label.text()
@@ -2814,6 +3183,34 @@ class HeirListWidget(MyTreeView, MessageBoxMixin):
return toolbar
+ """Widget for previewing Will documents before signing.
+
+ Provides functionality to:
+ - Render Will in human-readable format
+ - Highlight important clauses and conditions
+ - Show inheritance distribution
+ - Validate Will structure
+ - Generate PDF preview
+
+ Uses MyTreeView for:
+ - Column-based preview display
+ - Sorting and filtering
+ - Efficient rendering of large documents
+
+ Attributes:
+ bal_window (BalWindow): Reference to main window
+ main_window (QMainWindow): Parent window
+
+ Methods:
+ update(): Refresh preview display
+ validate(): Check Will structure
+ export_pdf(): Generate PDF document
+
+ Example:
+ preview = PreviewList(window, parent)
+ preview.update()
+ """
+
def update_will_settings(self):
try:
self.heir_locktime.set_locktime(self.bal_window.will_settings["locktime"])
@@ -3091,6 +3488,17 @@ class PreviewList(MyTreeView):
close_window.build_will_task()
will = {}
+ """PreviewDialog class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = PreviewDialog(required parameters)
+ """
+
for wid, w in self.bal_window.willitems.items():
if (
w.get_status("VALID")
@@ -3172,6 +3580,17 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
def update(self):
self.transactions_list.update()
+ """WillDetailDialog class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = WillDetailDialog(required parameters)
+ """
+
def is_hidden(self):
return self.isMinimized() or self.isHidden()
@@ -3272,6 +3691,17 @@ class WillDetailDialog(BalDialog):
toggle = _("Unhide")
self.toggle_replace_button.setText(f"{toggle} {_('replaced')}")
self.update()
+ """WillWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = WillWidget(required parameters)
+ """
+
def toggle_invalidated(self):
self.bal_window.bal_plugin.hide_invalidated()
@@ -3363,6 +3793,17 @@ class WillWidget(QWidget):
decoded_amount = Util.decode_amount(
self.will[w].we["base_fee"], self.parent.decimal_point
)
+ """WillExecutorListWidget class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = WillExecutorListWidget(required parameters)
+ """
+
detaillayout.addWidget(
qlabel(
@@ -3594,6 +4035,31 @@ class WillExecutorListWidget(MyTreeView):
items[self.Columns.BASE_FEE].setData(
url, self.ROLE_HEIR_KEY + self.Columns.BASE_FEE
)
+ """Widget for executing Will transactions.
+
+ Handles:
+ - Transaction construction
+ - Fee calculation
+ - Signature collection
+ - Transaction broadcasting
+ - Status monitoring
+
+ Coordinates with:
+ - WillExecutorListWidget: List of executors
+ - WillExecutorDialog: Detailed executor configuration
+ - BalWindow: Main application window
+
+ Attributes:
+ will (Will): Will being executed
+ executors (list): List of executor configurations
+ network (Network): Network interface
+
+ Methods:
+ execute(): Start transaction execution
+ stop(): Interrupt execution
+ validate(): Check transaction validity
+ """
+
items[self.Columns.INFO].setData(
url, self.ROLE_HEIR_KEY + self.Columns.INFO
)
@@ -3698,6 +4164,17 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
self.willexecutors_list.update,
)
+ """WillExecutorDialog class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = WillExecutorDialog(required parameters)
+ """
+
def update_willexecutors(self, wes=None):
if not wes:
wes = self.willexecutors_list
@@ -3733,6 +4210,17 @@ class WillExecutorDialog(BalDialog, MessageBoxMixin):
self.setMinimumSize(1000, 200)
vbox = QVBoxLayout(self)
+ """CheckAliveException class for Bitcoin After Life.
+
+ This class provides functionality for managing various Bitcoin After Life features.
+
+ Attributes:
+ See class implementation for attributes
+
+ Example:
+ instance = CheckAliveException(required parameters)
+ """
+
self.will_executor_list_widget = WillExecutorWidget(
self, self.bal_window, self.willexecutors_list
)
@@ -3759,4 +4247,20 @@ class CheckAliveException(Exception):
def __init__(self,timestamp_to_check):
self.timestamp_to_check = timestamp_to_check
def __str__(self):
+
+ def __del__(self):
+ """Explicit cleanup to prevent memory leaks.
+
+ This destructor ensures proper cleanup of:
+ - Message queue timer
+ - All widgets in the layout
+ - Network connections
+
+ Called automatically when the dialog is destroyed.
+ """
+ if hasattr(self, '_message_timer') and self._message_timer:
+ self._message_timer.stop()
+ self._message_timer.deleteLater()
+ self.clear_layout(self.labelsbox)
+
return "Check alive expired please update it: {}".format(datetime.fromtimestamp(self.timestamp_to_check).isoformat())