6 Commits

211
qt.py
View File

@@ -1271,6 +1271,41 @@ class _LockTimeEditor:
return cls.min_allowed_value <= x <= cls.max_allowed_value
<<<<<<< HEAD
=======
"""
HeirsLockTimeEdit - A custom QWidget for editing locktime values in the context of heirs distribution.
This widget combines raw locktime editing with date-based selection and provides
additional functionality for managing locktime values in a heir inheritance scenario.
Features:
- Supports both raw locktime values and human-readable date formats
- Emits valueEdited signal when the locktime value is edited
- Provides threshold-based validation for locktime values
- Integrates with heir distribution workflows
Behavior:
The class handles three types of locktime values:
1. **Timestamps**: Raw integer values representing Unix timestamps
2. **Day intervals**: Values ending with 'd' (e.g., '3d' = 3 days from now)
3. **Year intervals**: Values ending with 'y' (e.g., '2y' = 2 years from now)
Only these formats are valid:
- Examples: '1609459200', '3d', '2y'
- Invalid: 'invalid', '5m', '10x'
The widget automatically converts day/year intervals to appropriate timestamps.
Attributes:
valueEdited (pyqtSignal): Signal emitted when the locktime value is edited
locktime_threshold (int): Minimum threshold value for locktime (default: 50000000)
Args:
parent: Optional parent QWidget
default_index (int): Default index for the combo box (default: 1)
"""
class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
valueEdited = pyqtSignal()
locktime_threshold = 50000000
@@ -1557,6 +1592,38 @@ class BalDialog(WindowModalDialog):
self.setWindowIcon(read_QIcon_from_bytes(bal_plugin.read_file(icon)))
"""
BalWizardDialog - A custom QDialog that implements a multi-step wizard interface.
This dialog provides a structured, step-by-step workflow for complex operations
in the Bal Electrum plugin, guiding users through a sequence of pages with
forward/backward navigation and validation.
Features:
- Multi-page navigation with Previous/Next buttons
- Automatic validation before proceeding to next page
- Progress tracking with visual indicators
- Customizable page flow and validation rules
- Integration with BalDialog base class for consistent styling
Usage:
The wizard follows a standard pattern:
1. Initialize with a list of page constructors
2. Each page is responsible for its own setup and validation
3. The dialog manages navigation and state between pages
4. Finalize action is triggered when all pages are completed
Attributes:
pages (list): List of page constructors for the wizard
current_page (int): Index of the currently displayed page
page_widgets (list): List of instantiated page widgets
Args:
parent: Optional parent QWidget
title (str): Title to display in the dialog header
pages (list): List of page constructors (callables) for each step
"""
class BalWizardDialog(BalDialog):
def __init__(self, bal_window: "BalWindow"):
assert bal_window
@@ -2069,6 +2136,21 @@ class BalBuildWillDialog(BalDialog):
COLOR_OK = "#05ad05"
def __init__(self, bal_window, parent=None):
<<<<<<< HEAD
=======
"""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
"""
>>>>>>> origin/doc
if not parent:
parent = bal_window.window
BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will"))
@@ -2076,6 +2158,7 @@ class BalBuildWillDialog(BalDialog):
self.updatemessage.connect(self.msg_update)
self.bal_window = bal_window
self.bal_plugin = bal_window.bal_plugin
<<<<<<< HEAD
self.message_label = QLabel(_("Building Will:"))
self.vbox = QVBoxLayout(self)
self.vbox.addWidget(self.message_label,0)
@@ -2086,6 +2169,45 @@ class BalBuildWillDialog(BalDialog):
self.setMinimumHeight(100)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
self.labels = []
=======
# Main message label
self.message_label = QLabel(_("Building Will:"))
self.vbox = QVBoxLayout(self)
self.vbox.addWidget(self.message_label, 0)
# Container for dynamic messages
self.qwidget = QWidget(self)
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)
# 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
>>>>>>> origin/doc
self.check_row = None
self.inval_row = None
self.build_row = None
@@ -2535,6 +2657,7 @@ class BalBuildWillDialog(BalDialog):
w.deleteLater()
def msg_update(self):
<<<<<<< HEAD
self.clear_layout(self.labelsbox)
for label in self.labels:
label=label.replace("\n","<br>")
@@ -2542,6 +2665,75 @@ class BalBuildWillDialog(BalDialog):
self.labelsbox.addWidget(QLabel(label),1)
self.setMinimumHeight(30*(len(self.labels)+2))
=======
"""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)
# Process all queued messages
for text in self._message_queue:
try:
# Format text for rich display
formatted_text = text.replace("\n", "<br>")
# 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
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
>>>>>>> origin/doc
def get_text(self):
return self.message_label.text()
@@ -3759,4 +3951,23 @@ class CheckAliveException(Exception):
def __init__(self,timestamp_to_check):
self.timestamp_to_check = timestamp_to_check
def __str__(self):
<<<<<<< HEAD
=======
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)
>>>>>>> origin/doc
return "Check alive expired please update it: {}".format(datetime.fromtimestamp(self.timestamp_to_check).isoformat())