| name | pyqt-dialogs |
| description | PyQt/PySide6 dialogs - QFileDialog, QMessageBox, QInputDialog, QColorDialog, custom QDialog patterns |
| metadata | {"author":"mte90","version":"1.0.0","tags":["python","qt","pyqt","pyside","dialogs","ui"]} |
PyQt Dialogs
Standard and custom dialog patterns for PyQt/PySide6 applications.
Standard Dialogs
QFileDialog
from PySide6.QtWidgets import QFileDialog
filename, _ = QFileDialog.getOpenFileName(
self,
"Open File",
"/home/user",
"Images (*.png *.jpg);;Text Files (*.txt);;All Files (*)"
)
if filename:
print(f"Selected: {filename}")
filename, _ = QFileDialog.getSaveFileName(
self,
"Save File",
"/home/user/untitled.txt",
"Text Files (*.txt);;All Files (*)"
)
directory = QFileDialog.getExistingDirectory(
self,
"Select Directory",
"/home/user",
QFileDialog.Option.ShowDirsOnly
)
files, _ = QFileDialog.getOpenFileNames(
self,
"Open Files",
"/home/user",
"Images (*.png *.jpg)"
)
for f in files:
print(f)
options = QFileDialog.Option.DontUseNativeDialog
filename, _ = QFileDialog.getOpenFileName(self, "Open", "", "", options=options)
QMessageBox
from PySide6.QtWidgets import QMessageBox
reply = QMessageBox.question(
self,
"Confirm",
"Are you sure?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
print("User confirmed")
QMessageBox.information(self, "Info", "Operation completed successfully")
QMessageBox.warning(self, "Warning", "This action cannot be undone")
QMessageBox.critical(self, "Error", "Failed to connect to server")
QMessageBox.about(self, "About", "My App v1.0\n\nCopyright 2024")
QMessageBox.aboutQt(self)
msg = QMessageBox(self)
msg.setWindowTitle("Custom Dialog")
msg.setText("Continue?")
msg.setIcon(QMessageBox.Icon.Question)
msg.addButton("Yes", QMessageBox.ButtonRole.YesRole)
msg.addButton("No", QMessageBox.ButtonRole.NoRole)
msg.addButton("Cancel", QMessageBox.ButtonRole.RejectRole)
result = msg.exec()
print(f"Button role: {msg.buttonRole(msg.clickedButton())}")
QInputDialog
from PySide6.QtWidgets import QInputDialog, QLineEdit
text, ok = QInputDialog.getText(
self,
"Input",
"Enter name:",
QLineEdit.EchoMode.Normal,
"Default value"
)
if ok and text:
print(f"Name: {text}")
value, ok = QInputDialog.getInt(
self,
"Input",
"Enter age:",
25,
0,
120,
1
)
if ok:
print(f"Age: {value}")
price, ok = QInputDialog.getDouble(
self,
"Input",
"Enter price:",
0.0,
0.0,
1000.0,
2
)
if ok:
print(f"Price: ${price:.2f}")
items = ["Option 1", "Option 2", "Option 3"]
item, ok = QInputDialog.getItem(
self,
"Select",
"Choose an option:",
items,
0,
False
)
if ok:
print(f"Selected: {item}")
text, ok = QInputDialog.getMultiLineText(
self,
"Input",
"Enter description:",
"Default\ntext"
)
QColorDialog
from PySide6.QtWidgets import QColorDialog
from PySide6.QtGui import QColor
color = QColorDialog.getColor(
QColor(255, 0, 0),
self,
"Select Color"
)
if color.isValid():
print(f"Color: {color.name()}")
widget.setStyleSheet(f"background-color: {color.name()};")
color = QColorDialog.getColor(
QColor(255, 0, 0, 128),
self,
"Select Color with Alpha",
QColorDialog.ColorDialogOption.ShowAlphaChannel
)
options = (
QColorDialog.ColorDialogOption.ShowAlphaChannel |
QColorDialog.ColorDialogOption.NoButtons
)
color = QColorDialog.getColor(Qt.white, self, "Color", options)
QFontDialog
from PySide6.QtWidgets import QFontDialog
from PySide6.QtGui import QFont
font, ok = QFontDialog.getFont(
QFont("Arial", 12),
self,
"Select Font"
)
if ok:
print(f"Font: {font.family()}, Size: {font.pointSize()}")
widget.setFont(font)
font, ok = QFontDialog.getFont(
QFont(),
self,
"Select Font",
QFontDialog.FontDialogOption.MonospacedFonts
)
Custom Dialogs
Basic Custom Dialog
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QLineEdit, QDialogButtonBox, QFormLayout
)
class InputDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Input Dialog")
self.setMinimumWidth(400)
layout = QVBoxLayout(self)
form = QFormLayout()
self.name_edit = QLineEdit()
self.email_edit = QLineEdit()
form.addRow("Name:", self.name_edit)
form.addRow("Email:", self.email_edit)
layout.addLayout(form)
buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok |
QDialogButtonBox.StandardButton.Cancel
)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
def get_values(self):
return {
"name": self.name_edit.text(),
"email": self.email_edit.text()
}
def set_values(self, name="", email=""):
self.name_edit.setText(name)
self.email_edit.setText(email)
dialog = InputDialog(self)
dialog.set_values("John", "john@example.com")
if dialog.exec() == QDialog.DialogCode.Accepted:
values = dialog.get_values()
print(f"Name: {values['name']}, Email: {values['email']}")
Dialog with Validation
class ValidatedDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Validated Input")
layout = QVBoxLayout(self)
form = QFormLayout()
self.age_spin = QSpinBox()
self.age_spin.setRange(0, 120)
self.email_edit = QLineEdit()
form.addRow("Age:", self.age_spin)
form.addRow("Email:", self.email_edit)
layout.addLayout(form)
self.error_label = QLabel()
self.error_label.setStyleSheet("color: red;")
layout.addWidget(self.error_label)
self.buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok |
QDialogButtonBox.StandardButton.Cancel
)
self.buttons.accepted.connect(self.try_accept)
self.buttons.rejected.connect(self.reject)
layout.addWidget(self.buttons)
def try_accept(self):
if not self.validate():
return
self.accept()
def validate(self):
import re
email = self.email_edit.text()
if not email:
self.error_label.setText("Email is required")
return False
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
self.error_label.setText("Invalid email format")
return False
self.error_label.clear()
return True
def get_values(self):
return {
"age": self.age_spin.value(),
"email": self.email_edit.text()
}
Modeless Dialog
class SearchDialog(QDialog):
"""Non-modal (modeless) dialog that stays open."""
searchRequested = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Search")
self.setWindowFlags(
Qt.WindowType.Dialog |
Qt.WindowType.WindowCloseButtonHint
)
layout = QVBoxLayout(self)
self.search_edit = QLineEdit()
self.search_edit.setPlaceholderText("Enter search term...")
self.search_edit.returnPressed.connect(self.search)
self.search_btn = QPushButton("Search")
self.search_btn.clicked.connect(self.search)
layout.addWidget(self.search_edit)
layout.addWidget(self.search_btn)
def search(self):
term = self.search_edit.text()
if term:
self.searchRequested.emit(term)
search_dialog = SearchDialog(self)
search_dialog.searchRequested.connect(self.perform_search)
search_dialog.show()
Best Practices
- Use standard dialogs when possible - They're familiar and consistent
- Provide sensible defaults - Pre-fill common values
- Validate input before accepting - Show clear error messages
- Use QDialogButtonBox - Ensures correct button ordering per platform
- Set minimum size - Prevent dialogs from being too small
- Consider modeless dialogs - For search, find/replace, etc.
Qt 6 Dialogs
QDialog Modern Patterns
class MyDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Settings")
self.setModal(True)
layout = QVBoxLayout()
self.setLayout(layout)
btn_box = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
btn_box.accepted.connect(self.accept)
btn_box.rejected.connect(self.reject)
layout.addWidget(btn_box)
def get_value(self):
if self.result() == QDialog.Accepted:
return self.value
self.addNativeButton(QDialogButtonBox.StandardButton.Ok)
Modal vs Non-modal
dialog = MyDialog(parent)
result = dialog.exec()
dialog = MyDialog(parent)
dialog.show()
Best Practices Code Examples
button_box = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
dialog = QDialog(parent_window)
dialog = QDialog()
Advanced Dialogs
Multi-page Dialog
class MultiPageDialog(QDialog):
def __init__(self):
super().__init__()
self.pages = [Page1(), Page2(), Page3()]
self.current_page = 0
self.setup_ui()
def setup_ui(self):
main_layout = QVBoxLayout()
page_layout = QHBoxLayout()
prev_btn = QPushButton("← Prev")
next_btn = QPushButton("Next →")
prev_btn.clicked.connect(lambda: self.set_page(-1))
next_btn.clicked.connect(lambda: self.set_page(1))
page_layout.addWidget(prev_btn)
page_layout.addWidget(next_btn)
self.page_container = QWidget()
self.page_container_layout = QVBoxLayout(self.page_container)
self.page_container_layout.addWidget(self.pages[0])
main_layout.addLayout(page_layout)
main_layout.addWidget(self.page_container)
self.setLayout(main_layout)
def set_page(self, delta):
self.current_page += delta
if 0 <= self.current_page < len(self.pages):
self.page_container_layout.deleteWidget(self.pages[0])
self.page_container_layout.addWidget(self.pages[self.current_page])
else:
self.current_page = max(0, min(len(self.pages)-1, self.current_page))
References