Qt Designer is a fantastic tool for building your application's UI visually. But you'll quickly notice that some useful widgets — like QWebEngineView for displaying web content, or QVideoWidget for playing video — aren't available in the widget palette. Does that mean you can't use them with Qt Designer?
Not at all. Qt Designer supports a feature called widget promotion that lets you swap any placeholder widget for a custom or specialized widget at runtime. In this tutorial, you'll learn how to use this technique to add a QWebEngineView to a Qt Designer layout and load a web page — and the same approach works for any widget not found in the palette.
What is widget promotion?
Widget promotion is a way of telling Qt Designer: "This plain QWidget you see here? When the application actually runs, replace it with this other widget class instead."
You design your layout using a standard QWidget as a stand-in. Then you "promote" that widget to the class you actually want to use — like QWebEngineView. When your application loads the .ui file, PyQt6 creates the promoted widget class in place of the placeholder.
This works for any widget class that Qt (or a third-party library) provides, as long as it inherits from QWidget.
Installing QWebEngine
Before we start, make sure you have the QWebEngine module installed. If you're using PyQt6, install it with:
pip install PyQtWebEngine
This provides the QWebEngineView widget that we'll be using.
Setting up the layout in Qt Designer
Open Qt Designer and create a new Main Window form. We'll build a simple browser-like window with a QWebEngineView taking up most of the space. If you haven't already set up Qt Designer, see our guide on how to install Qt Designer standalone.
From the widget box on the left, drag a plain Widget (found under the "Containers" section) onto the central area of your main window. This QWidget is going to be our placeholder — the widget we'll promote to QWebEngineView.
Bring Your PyQt/PySide Application to Market — Specialized launch support for scientific and engineering software built using Python & Qt.
You can also add a QLineEdit at the top for a URL bar and a QPushButton next to it labeled "Go", to make things more interesting. Use a vertical layout on the central widget, with a horizontal layout holding the line edit and button at the top, and the placeholder widget below.
Give your widgets some sensible object names in the Property Editor:
- The
QLineEdit:urlBar - The
QPushButton:goButton - The placeholder
QWidget:webView
Your layout should look something like this:

Promoting the widget
Now for the important part — promoting the placeholder widget to QWebEngineView.
Right-click on the placeholder QWidget (the one you named webView) and select Promote to... from the context menu.
In the dialog that appears, fill in two fields:
- Promoted class name:
QWebEngineView - Header file:
PyQt6.QtWebEngineWidgets
The header file field tells PyQt6 where to import the class from. In C++ Qt, this would be an actual header file path. In PyQt6, it corresponds to the Python module path — so PyQt6.QtWebEngineWidgets is where QWebEngineView lives.

Click Add to add the promoted class to the list, then click Promote to apply it to your selected widget.
You'll notice the widget in Qt Designer still looks like a plain QWidget. That's expected — Qt Designer doesn't actually render the promoted widget. But if you check the Object Inspector, you'll see the class listed as QWebEngineView. The promotion is stored in the .ui file and will take effect when your application runs.
Save your file as browser.ui.
Loading the UI in Python
Now let's write the Python code to load this .ui file and make the web view actually do something. We'll use uic.loadUi() to load the Designer file directly.
import sys
from PyQt6.QtCore import QUrl
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6 import uic
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi("browser.ui", self)
# Load a default page.
self.webView.setUrl(QUrl("https://www.pythonguis.com"))
# Connect the Go button to navigate.
self.goButton.clicked.connect(self.navigate)
self.show()
def navigate(self):
url = self.urlBar.text()
if not url.startswith("http"):
url = "https://" + url
self.webView.setUrl(QUrl(url))
app = QApplication(sys.argv)
window = MainWindow()
app.exec()
When uic.loadUi() processes the .ui file, it sees that the webView widget was promoted to QWebEngineView with the import path PyQt6.QtWebEngineWidgets. It automatically imports the class and creates a QWebEngineView instance in place of the placeholder. You can then interact with it just as if you'd created it in code — calling .setUrl(), connecting signals, and so on.
Run this script, and you should see a window with a URL bar at the top and a fully functional web view below it, displaying the pythonguis.com website.
Using the alternative: pyuic5 compiled files
If you prefer to compile your .ui files to Python using pyuic5, the promotion still works the same way. Run:
pyuic5 browser.ui -o browser_ui.py
If you open the generated browser_ui.py file, you'll see that pyuic5 has automatically added the correct import at the bottom:
from PyQt6.QtWebEngineWidgets import QWebEngineView
And the widget creation code uses QWebEngineView instead of QWidget. Everything is handled for you.
To use the compiled file, adjust your Python code:
import sys
from PyQt6.QtCore import QUrl
from PyQt6.QtWidgets import QApplication, QMainWindow
from browser_ui import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.webView.setUrl(QUrl("https://www.pythonguis.com"))
self.goButton.clicked.connect(self.navigate)
self.show()
def navigate(self):
url = self.urlBar.text()
if not url.startswith("http"):
url = "https://" + url
self.webView.setUrl(QUrl(url))
app = QApplication(sys.argv)
window = MainWindow()
app.exec()
Both approaches — loadUi and compiled .py files — handle widget promotion automatically.
Promoting to other widget types
The same promotion technique works for any widget that isn't in Qt Designer's default palette. Here are a few common examples:
| Widget | Promoted class name | Header file (PyQt6) |
|---|---|---|
QWebEngineView |
QWebEngineView |
PyQt6.QtWebEngineWidgets |
QVideoWidget |
QVideoWidget |
PyQt6.QtMultimediaWidgets |
PlotWidget (pyqtgraph) |
PlotWidget |
pyqtgraph |
QChartView |
QChartView |
PyQt6.QtChart |
The process is always the same:
- Place a
QWidgetin your layout as a placeholder. - Right-click → Promote to...
- Enter the class name and the Python module it comes from.
- Click Add, then Promote.
For third-party libraries like pyqtgraph, the header file field is simply the Python package name — pyqtgraph — because that's where you'd import PlotWidget from. For a detailed walkthrough of embedding pyqtgraph widgets this way, see our tutorial on embedding pyqtgraph as custom widgets in PyQt6.
Complete working example without a .ui file
If you want to test the concept without using Qt Designer at all, here's a complete example that creates the same browser layout entirely in code. This can be useful for understanding what the .ui file is doing behind the scenes:
import sys
from PyQt6.QtCore import QUrl
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QLineEdit,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Simple Browser")
# URL bar and Go button.
self.url_bar = QLineEdit()
self.url_bar.setPlaceholderText("Enter a URL...")
self.url_bar.returnPressed.connect(self.navigate)
self.go_button = QPushButton("Go")
self.go_button.clicked.connect(self.navigate)
nav_layout = QHBoxLayout()
nav_layout.addWidget(self.url_bar)
nav_layout.addWidget(self.go_button)
# Web view.
self.web_view = QWebEngineView()
self.web_view.setUrl(QUrl("https://www.pythonguis.com"))
# Update the URL bar when the page changes.
self.web_view.urlChanged.connect(self.update_url_bar)
# Main layout.
layout = QVBoxLayout()
layout.addLayout(nav_layout)
layout.addWidget(self.web_view)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
self.show()
def navigate(self):
url = self.url_bar.text()
if not url.startswith("http"):
url = "https://" + url
self.web_view.setUrl(QUrl(url))
def update_url_bar(self, url):
self.url_bar.setText(url.toString())
app = QApplication(sys.argv)
window = MainWindow()
app.exec()
Run this and you'll get a working mini-browser with a URL bar, a Go button, and a web view displaying a page. This is exactly what the promoted widget in Qt Designer creates for you — the difference is that with Designer, you get to arrange the layout visually and let the promotion system handle the widget creation. For more complete browser examples, take a look at our web browser projects.
Summary
Qt Designer doesn't include every widget in its palette, but that doesn't limit what you can use. With widget promotion, you place a standard QWidget as a placeholder, then tell Designer which class it should become at runtime. This works seamlessly with both uic.loadUi() and pyuic5-compiled files, and it applies to any widget — whether it's part of Qt itself (like QWebEngineView or QVideoWidget) or comes from a third-party library (like pyqtgraph).
The process is always: add a QWidget, right-click, promote, and fill in the class name and module path. From there, your code can interact with the promoted widget exactly as if you'd created it by hand. To learn more about designing full application layouts with Qt Designer, see our Qt Designer GUI layout tutorial.
Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.