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
To use QSettings, you first need to create an instance. The most common way is to pass in your organization name and application name:
python
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:
python
print(settings.fileName())
On Linux, this might print something like:
python
/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:
python
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():
python
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:
python
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:
python
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():
python
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.
python
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 a QSettings object 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():
python
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():
python
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:
python
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:
python
settings.beginGroup('appearance')
theme = settings.value('theme', 'Light')
settings.endGroup()
Alternatively, you can use a / separator in the key name as a shorthand:
python
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:
python
app = QApplication(sys.argv)
app.setOrganizationName('MyCompany')
app.setApplicationName('MyApp')
After this, you can create QSettings objects without any arguments:
python
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:
python
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:
python
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:
python
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
QSettings object 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.
For an in-depth guide to building Python GUIs with PyQt6 see my book, Create GUI Applications with Python & Qt6.