Building PyQt5 GUIs in PyCharm with Qt Designer and pyuic
This guide shows how to design user interfaces with Qt Deisgner, configure PyCharm to use Designer and pyuic, and run the resulting PyQt5 application. It also includes a pure-code alternative without Designer.
1. Install the required tools
- Install PyCharm (Community or Professional).
- Install PyQt5 and Qt Designer tools in the interpreter used by the project:
# PyQt5 runtime and widgets
pip install PyQt5
# Qt Designer and related toolchain
pip install pyqt5-tools
Notes:
- On Windows, pyqt5-tools typically places Designer at:
- \Lib\site-packages\qt5_applications\Qt\bin\designer.exe
- On Linux/macOS,
pyuic5is usually on PATH after installing PyQt5. Designer may be available through system packages or pyqt5-tools.
To programmatically locate Designer when using pyqt5-tools on Windows:
python -c "import os, qt5_applications as q; print(os.path.join(os.path.dirname(q.__file__), 'Qt', 'bin', 'designer.exe'))"
2. Configure PyCharm External Tools
Setting up External Tools lets you launch Designer and convert .ui files without leaving the IDE.
2.1 Add Qt Designer
- File → Settings → Tools → External Tools → + (Add)
- Name: Qt Designer
- Program (Windows):
- $PyInterpreterDirectory$/Lib/site-packages/qt5_applications/Qt/bin/designer.exe
- Program (Linux/macOS):
- designer (if available on PATH) or the full path to Designer
- Working directory: $ProjectFileDir$
2.2 Add pyuic5 (UI compiler)
- File → Settings → Tools → External Tools → + (Add)
- Name: pyuic5
- Program (Windows): $PyInterpreterDirectory$/Scripts/pyuic5.exe
- Program (Linux/macOS): pyuic5
- Arguments:
- -o "$FileNameWithoutExtension$.py" "$FileName$"
- Working directory: $FileDir$
You can now access both from Tools → Exetrnal Tools in PyCharm.
3. Design a form with Qt Designer
- Tools → External Tools → Qt Designer.
- Create a "Main Window".
- Drop widgets (labels, line edits, push buttons) onto the form.
- Set objectName properties you’ll use in code, for example:
- edtUser, edtPassword, btnLogin, btnQuit
- File → Save As… (e.g., login.ui) inside your project.
4. Convert the .ui file to Python
In PyCharm, right‑click login.ui → Tools → External Tools → pyuic5. This generates login.py (or login_ui.py—depending on your file name) containing the Ui_MainWindow class.
5. Run the generated UI in a PyQt5 application
Create a new Python file (e.g., app.py) that imports the generated class and wires up logic.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
# Assuming pyuic5 created login.py with Ui_MainWindow class
from login import Ui_MainWindow
class LoginWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Wire up signals
if hasattr(self.ui, 'btnLogin'):
self.ui.btnLogin.clicked.connect(self.handle_login)
if hasattr(self.ui, 'btnQuit'):
self.ui.btnQuit.clicked.connect(self.close)
def handle_login(self):
user = getattr(self.ui, 'edtUser', None)
pwd = getattr(self.ui, 'edtPassword', None)
username = user.text() if user else ''
password = pwd.text() if pwd else ''
if not username:
QMessageBox.warning(self, 'Login', 'Enter a username.')
return
if not password:
QMessageBox.warning(self, 'Login', 'Enter a password.')
return
QMessageBox.information(self, 'Login', f'Welcome, {username}!')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = LoginWindow()
win.show()
sys.exit(app.exec_())
- The generated module name and class depend on the .ui file name. Adjust the import if needed.
- It’s common to keep the generated file untouched and put all behavior in your own subclass as shown.
6. Building the same UI purely in code (no Designer)
Below is an equivalent login window implemented programmatically with layouts. This approach avoids absolute positioning and is more maintainable.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication, QWidget, QLabel, QLineEdit,
QPushButton, QMessageBox, QGridLayout, QHBoxLayout, QVBoxLayout
)
class LoginForm(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Login')
self.resize(360, 180)
self._build_ui()
def _build_ui(self):
lbl_user = QLabel('Username:')
lbl_pass = QLabel('Password:')
self.inp_user = QLineEdit()
self.inp_user.setPlaceholderText('Your username')
self.inp_pass = QLineEdit()
self.inp_pass.setPlaceholderText('Your password')
self.inp_pass.setEchoMode(QLineEdit.Password)
btn_login = QPushButton('Login')
btn_exit = QPushButton('Exit')
btn_login.clicked.connect(self.on_login)
btn_exit.clicked.connect(self.close)
grid = QGridLayout()
grid.addWidget(lbl_user, 0, 0)
grid.addWidget(self.inp_user, 0, 1)
grid.addWidget(lbl_pass, 1, 0)
grid.addWidget(self.inp_pass, 1, 1)
buttons = QHBoxLayout()
buttons.addStretch(1)
buttons.addWidget(btn_login)
buttons.addWidget(btn_exit)
root = QVBoxLayout()
root.addLayout(grid)
root.addLayout(buttons)
self.setLayout(root)
def on_login(self):
u = self.inp_user.text().strip()
p = self.inp_pass.text()
if not u:
QMessageBox.warning(self, 'Login', 'Username is required.')
return
if not p:
QMessageBox.warning(self, 'Login', 'Password is required.')
return
QMessageBox.information(self, 'Login', f'Hello, {u}!')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = LoginForm()
w.show()
sys.exit(app.exec_())
7. When to use Designer vs code
- Designer + pyuic
- Visual layout and rapid prototyping
- Keeps UI structure separate from behavior
- Easy to iterate on widget placement and properties
- Pure code
- No generated files; full control via Python
- Great for dynamic UIs or complex programmatic layouts
- Slightly more verbose but version-control-friendly for small forms