Most desktop applications need to remember things between sessions. Maybe your user picked a dark theme, resized the window, or toggled a feature on or off. Without a way to save those choices, your app would forget everything the moment it closes. That's where QSettings comes in.
QSettings is a class provided by Qt (and available through PyQt6) that gives you a simple, cross-platform way to store and retrieve application settings. It handles all the platform-specific details for you — on Windows it uses the registry, on macOS it uses property list files, and on Linux it uses configuration files. You just read and write values, and Qt figures out the rest.
In this tutorial, we'll walk through everything you need to know to start using QSettings effectively in your PyQt6 applications.
- Creating a QSettings Object
- Storing Values
- Reading Values Back
- Checking if a Setting Exists
- A Complete Example
- Removing Settings
- Listing All Keys
- Organizing Settings with Groups
- Using QSettings with setOrganizationName and setApplicationName
- Managing Many Settings with a Dictionary
- Where Are Settings Stored?
- Working with QStandardPaths
- Summary
Creating a QSettings Object
To use QSettings, you first need to create an instance. The most common way is to pass in your organization name and application name:
from PyQt6.QtCore import QSettings
settings = QSettings('MyCompany', 'MyApp')
These two strings — the organization name and the application name — are used by Qt to determine where your settings are stored. They act like a namespace, keeping your app's settings separate from every other application on the system.
You can check exactly where your settings file lives by printing the file path:
print(settings.fileName())
On Linux, this might print something like:
/home/username/.config/MyCompany/MyApp.conf
On Windows, it would point to a registry path, and on macOS, a .plist file. You don't need to worry about these differences — QSettings handles it for you.
Storing Values
Saving a setting is as simple as calling setValue() with a key and a value:
settings.setValue('theme', 'Dark')
settings.setValue('font_size', 14)
settings.setValue('show_toolbar', True)
The key is a string that you'll use later to retrieve the value. The value can be a string, integer, boolean, list, or other common Python types. QSettings will serialize it appropriately.
That's it — the value is saved. When you call setValue(), the data is written to persistent storage (the exact timing depends on the platform, but it happens automatically).
Reading Values Back
To read a setting, use value():
theme = settings.value('theme')
print(theme) # 'Dark'
If the key doesn't exist (for example, the very first time your app runs), value() returns None by default. You can provide a default value as the second argument:
theme = settings.value('theme', 'Light')
Now if there's no theme key stored yet, you'll get 'Light' instead of None.
Handling Types
One thing that catches people off guard: QSettings stores everything as strings internally (at least when using INI-style backends on Linux). This means that when you read back a number or boolean, you might get a string instead of the type you expected.
To handle this, you can pass the type parameter:
font_size = settings.value('font_size', 14, type=int)
show_toolbar = settings.value('show_toolbar', True, type=bool)
By specifying type=int or type=bool, you ensure that the returned value is the correct Python type, regardless of how it was stored internally. This is especially important for booleans — without the type parameter, you might get the string 'true' instead of the boolean True.
Checking if a Setting Exists
Before reading a value, you might want to check whether it has been set at all. Use contains():
if settings.contains('theme'):
theme = settings.value('theme')
print(f'Found saved theme: {theme}')
else:
print('No theme saved yet, using default')
settings.setValue('theme', 'Light')
theme = 'Light'
This pattern is useful when you want to distinguish between "the user explicitly set this value" and "this is just the default."
A Complete Example
Let's put this all together in a small PyQt6 application that remembers the window size and position, as well as a user-selected theme. When you close the app, it saves these settings. When you reopen it, everything is restored.
import sys
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QComboBox,
QVBoxLayout, QWidget, QLabel
)
from PyQt6.QtCore import QSettings, QSize, QPoint
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.settings = QSettings('MyCompany', 'MyApp')
self.setWindowTitle("QSettings Demo")
# Create a simple UI with a theme selector
layout = QVBoxLayout()
layout.addWidget(QLabel("Choose a theme:"))
self.theme_combo = QComboBox()
self.theme_combo.addItems(['Light', 'Dark', 'Blue'])
layout.addWidget(self.theme_combo)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
# Restore settings
self.load_settings()
def load_settings(self):
# Restore window size and position
size = self.settings.value('window_size', QSize(400, 300))
position = self.settings.value('window_position', QPoint(100, 100))
self.resize(size)
self.move(position)
# Restore theme selection
theme = self.settings.value('theme', 'Light')
index = self.theme_combo.findText(theme)
if index >= 0:
self.theme_combo.setCurrentIndex(index)
def save_settings(self):
self.settings.setValue('window_size', self.size())
self.settings.setValue('window_position', self.pos())
self.settings.setValue('theme', self.theme_combo.currentText())
def closeEvent(self, event):
self.save_settings()
super().closeEvent(event)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Run this app, move or resize the window, select a different theme from the dropdown, then close the app. When you run it again, the window should appear in the same position and size, with the same theme selected.
Let's walk through what's happening:
- In
__init__, we create aQSettingsobject with our organization and app names. load_settings()reads values from persistent storage and applies them to the window and widgets. Notice how we pass default values (QSize(400, 300),QPoint(100, 100),'Light') so the app has sensible starting values on the very first run.save_settings()writes the current window size, position, and theme to settings.closeEvent()is a built-in Qt method that gets called when the window is about to close. We override it to save our settings right before that happens.
Removing Settings
If you need to delete a stored setting, use remove():
settings.remove('theme')
This removes the theme key entirely. After this, settings.contains('theme') would return False.
Listing All Keys
To see everything that's currently stored, use allKeys():
keys = settings.allKeys()
print(keys) # ['theme', 'font_size', 'show_toolbar', ...]
This can be handy for debugging, or if you want to iterate over all settings to display them in a preferences dialog.
Organizing Settings with Groups
As your app grows, you might end up with a lot of settings. QSettings supports groups, which let you organize keys into sections — similar to folders:
settings.beginGroup('appearance')
settings.setValue('theme', 'Dark')
settings.setValue('font_size', 14)
settings.endGroup()
settings.beginGroup('network')
settings.setValue('timeout', 30)
settings.setValue('retry_count', 3)
settings.endGroup()
When you read them back, you use the same group:
settings.beginGroup('appearance')
theme = settings.value('theme', 'Light')
settings.endGroup()
Alternatively, you can use a / separator in the key name as a shorthand:
settings.setValue('appearance/theme', 'Dark')
theme = settings.value('appearance/theme', 'Light')
Both approaches produce the same result. The slash syntax is a bit more concise, while beginGroup()/endGroup() is cleaner when you're reading or writing several settings in the same group at once.
Using QSettings with setOrganizationName and setApplicationName
Instead of passing the organization and app names every time you create a QSettings object, you can set them once on the QApplication:
app = QApplication(sys.argv)
app.setOrganizationName('MyCompany')
app.setApplicationName('MyApp')
After this, you can create QSettings objects without any arguments:
settings = QSettings()
It will automatically use the organization and application names you set. This is especially convenient in larger applications where you create QSettings in multiple places — you only need to define the names once at startup.
Managing Many Settings with a Dictionary
If your application has a lot of settings, checking each one individually can get repetitive. A cleaner approach is to define your defaults in a dictionary and loop through them:
DEFAULTS = {
'theme': 'Light',
'font_size': 14,
'show_toolbar': True,
'language': 'English',
'auto_save': True,
'auto_save_interval': 5,
}
settings = QSettings('MyCompany', 'MyApp')
# Load settings with defaults
config = {}
for key, default in DEFAULTS.items():
config[key] = settings.value(key, default, type=type(default))
print(config)
By using type=type(default), each value is automatically cast to the same type as its default. This keeps everything tidy and makes it easy to add new settings later — just add another entry to the dictionary.
Where Are Settings Stored?
If you're curious about where QSettings puts your data, or if you need to find the settings file for debugging, here's a quick summary:
| Platform | Storage Location |
|---|---|
| Windows | Registry under HKEY_CURRENT_USER\Software\MyCompany\MyApp |
| macOS | ~/Library/Preferences/com.mycompany.MyApp.plist |
| Linux | ~/.config/MyCompany/MyApp.conf |
You can always check the exact path using:
print(settings.fileName())
On Linux and macOS, the settings file is a plain text file that you can open and inspect directly, which is helpful for debugging.
Working with QStandardPaths
QSettings tells you where settings are stored, but sometimes you need to know about other standard locations — where to store cached data, application data, or downloaded files. Qt provides QStandardPaths for this:
from PyQt6.QtCore import QStandardPaths
# Where to store app configuration
config_path = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation)
print(f'Config: {config_path}')
# Where to store app data
data_path = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
print(f'Data: {data_path}')
# Where to store cached files
cache_path = QStandardPaths.writableLocation(QStandardPaths.CacheLocation)
print(f'Cache: {cache_path}')
QStandardPaths is separate from QSettings, but they complement each other well. Use QSettings for simple key-value preferences, and QStandardPaths when you need to store actual files (databases, logs, downloaded content) in the right platform-appropriate location.
Summary
QSettings gives you a clean, cross-platform way to persist user preferences in your PyQt6 applications. Here's a quick recap of the essentials:
- Create a
QSettingsobject with your organization and app name. - Use
setValue(key, value)to save a setting. - Use
value(key, default, type=...)to read a setting, with a default fallback and explicit type. - Use
contains(key)to check if a setting exists. - Use
remove(key)to delete a setting. - Override
closeEvent()on your main window to save settings when the app closes. - Organize related settings with groups using
beginGroup()/endGroup()or/in key names.
Once you're comfortable with these basics, you'll find that QSettings quietly handles one of those essential-but-tedious parts of desktop app development — letting you focus on the interesting stuff.
PyQt/PySide 1:1 Coaching with Martin Fitzpatrick
Save yourself time and frustration. Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.