What happens after defining `app`, but before executing it?

Understanding the relationship between QApplication, widgets, and the event loop in PyQt6
Heads up! You've already completed this tutorial.

I've just started learning PyQt and I don't understand how QApplication, .show(), and app.exec() work together. When we call window.show(), what does it show the window on? We haven't passed any arguments to .show(). Does PyQt automatically recognize the QApplication instance and display everything onto it?

This is a great question, and one that many people run into when they first start working with PyQt6. The relationship between QApplication, your widgets, and the event loop can feel a bit mysterious at first. Let's walk through what's actually happening, line by line.

A typical PyQt6 startup

Here's a minimal PyQt6 application:

python
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel


class MainWindow(QMainWindow):
    def __init__(self, text):
        super().__init__()
        self.setWindowTitle("My App")
        label = QLabel(text)
        self.setCentralWidget(label)


app = QApplication(sys.argv)

window = MainWindow("Hello, World!")
window.show()

app.exec()

There are four things happening here:

  1. QApplication(sys.argv) creates the application object.
  2. MainWindow("Hello, World!") creates a window widget.
  3. window.show() marks the window as visible.
  4. app.exec() starts the event loop.

Each of these steps does something specific, and understanding the order matters.

Creating QApplication

python
app = QApplication(sys.argv)

This line creates the QApplication object, which sets up everything Qt needs to function: the event queue, platform integration, display handling, and more. There can only ever be one QApplication per process.

You don't need to pass this app object into your widgets. Once it exists, Qt can find it internally — every widget in your program is automatically associated with the running QApplication. That's why you never see something like window.show(app). The connection is implicit.

If you're wondering about the sys.argv part, that allows Qt to process command line arguments passed to your application.

Creating your window

python
window = MainWindow("Hello, World!")

This creates the MainWindow object in memory. The QMainWindow is constructed, the QLabel is created and set as the central widget. But at this point, nothing is visible on screen. The window exists as a Python object with all its properties set up, but it hasn't been asked to appear yet.

Calling .show()

python
window.show()

This tells Qt that you'd like this window to be visible. It marks the widget's visibility flag, and the window frame may appear on screen. However, the contents of the window — the label, any buttons, layouts — won't actually be drawn yet. That's because drawing requires the event loop to be running, and we haven't started it.

So .show() is more like raising your hand and saying "I'm ready to be seen" rather than actually painting pixels on the screen.

Starting the event loop with app.exec()

python
app.exec()

This is where everything comes to life. app.exec() starts Qt's event loop — a continuous loop that waits for things to happen (mouse clicks, keyboard input, window resizes, repaint requests) and processes them one at a time.

When the event loop starts, Qt notices that your window has been marked as visible and processes the pending paint events. The window and all its contents are drawn on screen. From this point on, you can interact with the application — click buttons, type text, resize windows — and Qt will handle each interaction as an event.

Your Python code blocks on the app.exec() line. That means any code you write after app.exec() won't run until the application is closed and the event loop ends.

Seeing it in action

To make this sequence really concrete, here's a version with time.sleep() calls and print statements so you can observe each stage:

python
import sys
import time
from PyQt6.QtWidgets import QApplication, QPushButton

app = QApplication(sys.argv)

print("1. Creating the button widget...")
window = QPushButton("Hello, World!")
time.sleep(5)

print("2. Calling .show() — the window frame may appear, but the button won't be drawn yet...")
window.show()
time.sleep(5)

print("3. Starting the event loop — now the button becomes fully visible and interactive!")
app.exec()

print("4. The event loop has ended — the application was closed.")

Run this and watch what happens during each 5-second pause:

  • After step 1, nothing appears on screen. The button exists only as an object in memory.
  • After step 2, you might see an empty window frame, or a partially rendered window. The button text won't be drawn because the event loop isn't processing paint events yet.
  • After step 3, the button appears fully rendered and you can click it. The event loop is now running and processing all the events.
  • Step 4 only prints after you close the window, because app.exec() blocks until the application exits.

Why does it work this way?

GUI applications are fundamentally different from simple scripts that run top to bottom. A GUI needs to sit and wait for the user to do something — click a button, move a mouse, type a key — and respond to each action. That's what the event loop does.

Qt uses an event queue to manage this. Every action — a mouse click, a window resize, a request to repaint a widget — gets placed on the queue as an event. The event loop picks events off the queue one by one and dispatches them to the appropriate widget for handling. To learn more about how Qt handles these interactions, see our tutorial on signals, slots and events.

Before app.exec() is called, events can accumulate on the queue, but nothing processes them. That's why .show() alone doesn't fully render your window. The repaint event goes onto the queue, but it sits there waiting until the event loop starts.

The full picture

Here's a complete working example that ties everything together. It creates a window with a label and a button, and the button prints a message when clicked:

python
import sys
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QLabel,
    QPushButton, QVBoxLayout, QWidget,
)


class MainWindow(QMainWindow):
    def __init__(self, text):
        super().__init__()
        self.setWindowTitle("My App")

        label = QLabel(text)
        button = QPushButton("Click me")
        button.clicked.connect(self.button_clicked)

        layout = QVBoxLayout()
        layout.addWidget(label)
        layout.addWidget(button)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def button_clicked(self):
        print("Button was clicked!")


# 1. Create the application object (sets up Qt internals)
app = QApplication(sys.argv)

# 2. Create the window (builds widgets in memory, nothing visible yet)
window = MainWindow("Hello, World!")

# 3. Mark the window as visible (queues a paint event)
window.show()

# 4. Start the event loop (processes events, draws the window, handles clicks)
app.exec()

To summarize: QApplication sets up the Qt system. Creating widgets builds them in memory. Calling .show() marks them as ready to display. And app.exec() starts the event loop that actually draws everything and handles all user interaction. Each step builds on the last, and they all need to happen in this order for your application to work.

If you're new to PyQt6 and want to continue building on these fundamentals, take a look at our guide to creating your first window and the introduction to widgets.

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

PyQt/PySide Development Services

Stuck in development hell? I'll help you get your project focused, finished and released. Benefit from years of practical experience releasing software with Python.

Find out More

Martin Fitzpatrick

What happens after defining `app`, but before executing it? was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.