Skip to content

05 widget management

Zuko edited this page Jan 21, 2026 · 1 revision

WidgetManager - Widget Access Helper

Simplified widget access với dot notation, signal suppression, và config persistence

Overview

WidgetManager cung cấp utilities để:

  • Dot-notation widget access (get('parent.child'))
  • Signal suppression during updates
  • Config persistence (save/load widget state)
  • Type-safe widget value access

API Reference

Access

# Via controller
self.widgetManager.get('widgetName')
self.widgetManager.get('parent.child.widget')

Set Value

self.widgetManager.set('widgetName', value, saveToConfig=False)

Load from Config

self.widgetManager.load('widgetName', default=None)

Signal Suppression

self.widgetManager.doActionSuppressSignal('widgetName', lambda w: w.setValue(50))

Usage Examples

Basic Widget Access

from core import BaseController, BaseCtlHandler

class MyControllerHandler(BaseCtlHandler):
    def onSaveClicked(self):
        # Get widget
        input = self.widgetManager.get('usernameInput')
        text = input.text()
        
        # Nested access
        nestedWidget = self.widgetManager.get('parent.child.widget')

Set Widget Values

class MyHandler(BaseCtlHandler):
    def onLoadData(self):
        # Set QLineEdit
        self.widgetManager.set('nameInput', 'John Doe')
        
        # Set QSpinBox
        self.widgetManager.set('ageSpinBox', 25)
        
        # Set QCheckBox
        self.widgetManager.set('agreeCheckbox', True)

Config Persistence

class SettingsHandler(BaseCtlHandler):
    def onSaveSettings(self):
        # Save to config
        self.widgetManager.set('themeComboBox', 'dark', saveToConfig=True)
        self.widgetManager.set('autoSaveCheckbox', True, saveToConfig=True)
    
    def onLoadSettings(self):
        # Load from config
        self.widgetManager.load('themeComboBox', default='light')
        self.widgetManager.load('autoSaveCheckbox', default=False)

Config key format: {ControllerName}.{widgetName}

Example: MainController.themeComboBox

Signal Suppression

class MyHandler(BaseCtlHandler):
    def updateSliderWithoutSignal(self):
        # Suppress valueChanged signal
        self.widgetManager.doActionSuppressSignal(
            'volumeSlider',
            lambda slider: slider.setValue(75)
        )
        
        # Or with widget parameter
        def updateWidget(widget):
            widget.setValue(75)
            widget.setEnabled(False)
        
        self.widgetManager.doActionSuppressSignal('volumeSlider', updateWidget)

Supported Widget Types

Auto Value Handling

QSpinBox / QDoubleSpinBox:

self.widgetManager.set('spinBox', 42)  # Calls setValue()
value = self.widgetManager.get('spinBox').value()

QLineEdit / QLabel:

self.widgetManager.set('lineEdit', 'text')  # Calls setText()
text = self.widgetManager.get('lineEdit').text()

QCheckBox / QRadioButton:

self.widgetManager.set('checkbox', True)  # Calls setChecked()
checked = self.widgetManager.get('checkbox').isChecked()

Best Practices

✅ DO

# Use descriptive widget names
self.widgetManager.get('usernameInput')
self.widgetManager.get('saveButton')

# Use dot notation for nested widgets
self.widgetManager.get('settingsPanel.themeComboBox')

# Suppress signals when programmatically updating
self.widgetManager.doActionSuppressSignal('slider', lambda w: w.setValue(50))

# Save important settings to config
self.widgetManager.set('theme', 'dark', saveToConfig=True)

# Load with defaults
self.widgetManager.load('theme', default='light')

❌ DON'T

# Don't use set() with dot notation
self.widgetManager.set('parent.child', value)  # NotImplementedError

# Don't forget signal suppression for programmatic updates
slider = self.widgetManager.get('slider')
slider.setValue(50)  # Triggers valueChanged signal!

# Don't access widgets before setupUi
class MyController(BaseController):
    def __init__(self):
        super().__init__()
        widget = self.widgetManager.get('button')  # Error! setupUi not called

Common Patterns

Form Data Collection

class FormHandler(BaseCtlHandler):
    def collectFormData(self):
        return {
            'name': self.widgetManager.get('nameInput').text(),
            'age': self.widgetManager.get('ageSpinBox').value(),
            'email': self.widgetManager.get('emailInput').text(),
            'subscribe': self.widgetManager.get('subscribeCheckbox').isChecked()
        }

Batch Widget Update

class DataHandler(BaseCtlHandler):
    def loadUserData(self, userData):
        # Suppress all signals during batch update
        widgets = {
            'nameInput': userData['name'],
            'ageSpinBox': userData['age'],
            'emailInput': userData['email']
        }
        
        for widgetName, value in widgets.items():
            self.widgetManager.doActionSuppressSignal(
                widgetName,
                lambda w, v=value: self._setValue(w, v)
            )
    
    def _setValue(self, widget, value):
        if hasattr(widget, 'setText'):
            widget.setText(str(value))
        elif hasattr(widget, 'setValue'):
            widget.setValue(value)

Settings Persistence

class SettingsController(BaseController, QWidget):
    slot_map = {
        'saveClicked': ['saveButton', 'clicked'],
        'loadClicked': ['loadButton', 'clicked']
    }

class SettingsHandler(BaseCtlHandler):
    SETTINGS_WIDGETS = ['theme', 'language', 'autoSave', 'fontSize']
    
    def onSaveClicked(self):
        for widget in self.SETTINGS_WIDGETS:
            value = self._getWidgetValue(widget)
            self.widgetManager.set(widget, value, saveToConfig=True)
    
    def onLoadClicked(self):
        for widget in self.SETTINGS_WIDGETS:
            self.widgetManager.load(widget)
    
    def _getWidgetValue(self, widgetName):
        widget = self.widgetManager.get(widgetName)
        if hasattr(widget, 'text'):
            return widget.text()
        elif hasattr(widget, 'value'):
            return widget.value()
        elif hasattr(widget, 'isChecked'):
            return widget.isChecked()

Related Documentation

Clone this wiki locally