diff --git a/qt.py b/qt.py index 86e1b35..c8b7343 100644 --- a/qt.py +++ b/qt.py @@ -2069,6 +2069,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 +2088,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 +2574,73 @@ 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 + 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() @@ -3759,4 +3858,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())