I'm running PyQt5 on macOS Mojave and having redraw issues. Labels, text fields, and images don't update on events unless I click on another window (like my IDE or terminal). When clearing text in a field, it half-clears, leaving pixel fragments. The updated values are being passed correctly (I can see them via print statements), but the display doesn't refresh. What's going on?
This is a well-known issue that affects PyQt applications on certain versions of macOS. The good news is that there are straightforward solutions. Let's walk through what causes this and how to fix it.
Why Does This Happen?
macOS has its own rendering system for drawing windows and widgets on screen. Sometimes, PyQt and the macOS display layer get out of sync—your code updates a widget's value, but macOS doesn't know it needs to repaint that area of the window. The result is visual artifacts: half-drawn text, stale labels, or widgets that only look correct after you switch to another application and back.
This problem has been especially common on macOS Mojave (10.14) and some later versions, particularly when using certain versions of Qt.
Force a Repaint with update() or repaint()
If a widget isn't redrawing after you change its content, you can ask it to refresh explicitly. Every Qt widget has two methods for this:
.update()— schedules a repaint. Qt will repaint the widget the next time it processes events. This is the preferred approach in most cases..repaint()— forces an immediate repaint. Use this only if.update()isn't enough.
For example, if you're updating a QLabel and the display isn't changing:
self.label.setText("New value")
self.label.update()
Or, if the parent widget or layout needs to refresh:
self.label.setText("New value")
self.centralWidget().update()
Process Pending Events
Sometimes the issue is that Qt's event loop hasn't had a chance to process the repaint. If you're doing work in a slot (a function connected to a signal), the event loop is paused until that function returns. Calling QApplication.processEvents() gives Qt a moment to handle pending repaints:
from PyQt6.QtWidgets import QApplication
self.label.setText("Updated!")
QApplication.processEvents()
Use this sparingly—it's a helpful workaround, but if you find yourself needing it frequently, it may be a sign that long-running work should be moved to a background thread.
Purchasing Power Parity
Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]Set the QT_MAC_WANTS_LAYER Environment Variable
For macOS Mojave specifically, a common and effective fix is to set the environment variable QT_MAC_WANTS_LAYER to 1. This tells Qt to use Core Animation layers for rendering, which resolves many display glitches on macOS.
You can set this at the very top of your script, before creating your QApplication:
import os
os.environ["QT_MAC_WANTS_LAYER"] = "1"
Or, you can set it in your terminal before running your script:
export QT_MAC_WANTS_LAYER=1
python my_app.py
This single change fixes the problem for many people on macOS Mojave. In later versions of Qt (Qt 6 / PyQt6), this behavior is enabled by default, so you may not need it if you upgrade.
Upgrade Qt and PyQt
Many of the macOS rendering bugs were fixed in newer versions of Qt. If you're currently using PyQt5 with an older Qt version, consider:
-
Updating PyQt5 and its dependencies to the latest available versions:
sh pip install --upgrade PyQt5 -
Switching to PyQt6, which uses Qt 6 and includes many macOS-specific rendering fixes:
sh pip install PyQt6PyQt6 works very similarly to PyQt5. Most code transfers over with only small changes (such as how enums are accessed). If you're starting out, building on PyQt6 from the beginning will save you from running into these older platform bugs.
Upgrade macOS
Some of these rendering issues were resolved in macOS updates after Mojave. If upgrading your operating system is an option, moving to macOS Catalina (10.15) or later can help. Combined with an updated version of Qt, this eliminates most of the redraw problems.
Complete Working Example
Here's a small application that updates a label and an image on a button press. It includes the QT_MAC_WANTS_LAYER fix and an explicit .update() call, so it should work correctly even on macOS Mojave:
import os
os.environ["QT_MAC_WANTS_LAYER"] = "1"
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Redraw Test")
self.label = QLabel("Click the button to update this text.")
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label = QLabel()
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setFixedSize(200, 200)
self.image_label.setStyleSheet("background-color: #ddd;")
self.button = QPushButton("Update")
self.button.clicked.connect(self.update_display)
self.click_count = 0
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.image_label)
layout.addWidget(self.button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def update_display(self):
self.click_count += 1
self.label.setText(f"Button clicked {self.click_count} time(s).")
# Create a simple colored pixmap as a visual update.
colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6"]
color = colors[self.click_count % len(colors)]
pixmap = QPixmap(200, 200)
pixmap.fill(Qt.GlobalColor.white)
from PyQt6.QtGui import QPainter, QBrush, QColor
painter = QPainter(pixmap)
painter.setBrush(QBrush(QColor(color)))
painter.drawRect(10, 10, 180, 180)
painter.end()
self.image_label.setPixmap(pixmap)
# Force a repaint to ensure the display updates on macOS.
self.label.update()
self.image_label.update()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Run this script and click the button. The label text and the colored square should both update immediately each time, without needing to switch windows.
Summary
| Fix | When to use it |
|---|---|
os.environ["QT_MAC_WANTS_LAYER"] = "1" |
First thing to try on macOS Mojave |
.update() on widgets |
When a specific widget isn't repainting |
QApplication.processEvents() |
When updates are delayed until a function returns |
| Upgrade PyQt / Qt | Newer versions have macOS rendering fixes built in |
| Upgrade macOS | Later macOS versions resolved some rendering bugs |
If you're just getting started with Python GUI development, the most painless path forward is to use PyQt6 or PySide6 on a reasonably up-to-date version of macOS. Most of these rendering quirks simply don't appear with that combination.
Bring Your PyQt/PySide Application to Market — Specialized launch support for scientific and engineering software built using Python & Qt.