pyqt-core · v2.0.0 · 2026-10-28 · sha256 7ad8c14e0f756ac4

pyqt-core v2.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/7ad8c14e0f756ac4.

---
name: pyqt-core
description: Use when working with PyQt/PySide6 QtCore - signals, slots, properties, timers, settings, file I/O
metadata:
  author: mte90
  version: 2.0.0
  tags:
    - python
    - qt
    - pyqt
    - pyside
    - core
    - signals
    - slots
---

> **Objective:** Use this skill to reduce missed signal/slot connections, wrong connection-type crashes, QSettings scope mismatches, and timer leak incidents by enforcing connection-type rules for cross-thread communication, queue-aware patterns for worker threads, scope discipline for QSettings groups/arrays, and timer lifecycle checks with parent ownership.

# PyQt Core - QtCore Module

QtCore provides core non-GUI functionality: signals/slots, timers, settings, and file I/O.

## Critical Rules (Do X Because Y)

### 1. Use QueuedConnection for Cross-Thread Signals

**Do** use `Qt.QueuedConnection` when emitting signals from worker threads to UI slots because direct connections execute in the emitter's thread, causing crashes when slots access thread-unsafe UI objects.

```python
from PySide6.QtCore import Qt, QObject, Signal, QThread

class Worker(QThread):
    dataReady = Signal(dict)  # Declared in worker thread
    
    def run(self):
        data = self.expensiveOperation()
        self.dataReady.emit(data, Qt.QueuedConnection)  # Queue to UI thread

class MainWindow(QWidget):
    def setupWorker(self):
        self.worker = Worker()
        self.worker.dataReady.connect(self.handleData, Qt.QueuedConnection)
        self.worker.start()
```

**Failure mode:** Segmentation fault or Qt runtime error "QMetaObject::invokeMethod: Unable to unmarshal value" when DirectConnection crosses thread boundary.

### 2. Block Signals During Batch Updates

**Do** call `blockSignals(True)` before batch updates because each signal emission triggers connected slots, causing redundant processing and performance degradation.

```python
def populateTable(self, items):
    self.table.blockSignals(True)
    try:
        self.table.setRowCount(len(items))
        for i, item in enumerate(items):
            self.table.setItem(i, 0, QTableWidgetItem(item))
    finally:
        self.table.blockSignals(False)
```

**Failure mode:** Slot called N times for N row additions instead of once after batch complete.

### 3. Use Parent Ownership for Timers

**Do** pass parent to `QTimer(parent)` because parent ownership ensures automatic cleanup on widget destruction, preventing timer leaks and crashes from dangling timer references.

```python
class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.timer = QTimer(self)  # Self is parent - auto-cleanup
        self.timer.timeout.connect(self.onTimeout)
```

**Failure mode:** Timer continues firing after widget destroyed → crash on slot access.

### 4. Provide Defaults When Reading Settings

**Do** provide default values in `settings.value(key, default)` because missing keys return `None` without defaults, causing type errors downstream.

```python
# Safe - returns int 12 if key missing
font_size = settings.value("editor/fontSize", 10, type=int)

# Unsafe - returns None if missing, crashes on int comparison
font_size = settings.value("editor/fontSize")  # type: ignore
```

**Failure mode:** `TypeError: '>' not supported between instances of 'NoneType' and 'int'`.

### 5. Use IniFormat for Portable Settings

**Do** use `QSettings.IniFormat` for cross-platform config files because NativeFormat stores in platform-specific locations (Windows registry, macOS plist), making settings non-portable.

```python
# Portable - same file on all platforms
settings = QSettings("config.ini", QSettings.IniFormat)

# Platform-specific - different storage per OS
settings = QSettings("MyCompany", "MyApp")  # Native format
```

**Failure mode:** Settings appear "lost" when moving between platforms or after OS reinstall.

### 6. End Groups/Arrays in Finally Blocks

**Do** wrap `beginGroup/endGroup` and `beginWriteArray/endArray` in try-finally because exceptions leave settings in incomplete state, corrupting the file.

```python
def saveSettings(self):
    settings = QSettings("config.ini", QSettings.IniFormat)
    try:
        settings.beginGroup("editor")
        settings.setValue("fontSize", 12)
        settings.endGroup()
    finally:
        settings.endGroup()  # Ensures cleanup even on error
```

**Failure mode:** Corrupted INI file with unclosed groups preventing future reads.

### 7. Stop Timers Before Destruction

**Do** explicitly stop timers in `closeEvent` because while parent ownership handles cleanup, stopping timers prevents late emissions during destruction sequence.

```python
def closeEvent(self, event):
    self.timer.stop()  # Stop before parent cleanup
    super().closeEvent(event)
```

**Failure mode:** Timer fires during widget teardown → slot accesses destroyed objects.

### 8. Check File Existence Before Operations

**Do** check `QFile.exists()` before read operations because `open()` silently fails on missing files, causing data loss if write mode is used accidentally.

```python
if QFile.exists("data.txt"):
    file = QFile("data.txt")
    if file.open(QIODevice.ReadOnly):
        data = file.readAll()
        file.close()
else:
    logger.warning("Data file not found, using defaults")
```

**Failure mode:** Silent data loss when file missing and code proceeds with empty data.

## Signal/Slot Reference

See `references/signals-slots.md` for complete signal declaration, slot declaration, and connection syntax.

## QTimer Reference

See `references/timers.md` for complete timer API including single-shot, periodic, and timer properties.

## QSettings Reference

See `references/settings.md` for complete settings API including groups, arrays, and format options.

## File I/O Reference

See `references/file-io.md` for complete file operations including QFile, QTextStream, and QDir.

## Deep Dives

- `references/signals-slots.md` - Signal/slot syntax, connection types, best practices
- `references/timers.md` - QTimer API, single-shot vs periodic, timer types
- `references/settings.md` - QSettings API, groups, arrays, formats
- `references/file-io.md` - QFile, QTextStream, QDir operations

## References

- **Qt for Python QtCore**: https://doc.qt.io/qtforpython-6/PySide6/QtCore/
- **Signals and Slots**: https://doc.qt.io/qtforpython-6/signalsslot.html
- **QTimer**: https://doc.qt.io/qtforpython-6/PySide6/QtCore/QTimer.html
- **QSettings**: https://doc.qt.io/qtforpython-6/PySide6/QtCore/QSettings.html
- **QFile**: https://doc.qt.io/qtforpython-6/PySide6/QtCore/QFile.html