Fixing PyQt Windows That Won't Open or Get Stuck in Loops
In a Python developer community I participate in, a user recently ran into a frustrating issue: their PyQt script would run without errors, but the window would never appear. The process remained active in the task manager, seemingly “stuck in a loop,” but the UI was nowhere to be found.
This is a classic “silent failure” in desktop GUI development. Here is how to diagnose and fix the most common causes for this behavior.
The Original Question
Section titled “The Original Question”“I’m trying to launch a simple PyQt6 window. I’ve initialized QApplication and called .show() on my widget, but nothing happens. The terminal just sits there, and I have to kill the process. Is my while loop breaking it?”
Immediate Fix: The Two Most Common Culprits
Section titled “Immediate Fix: The Two Most Common Culprits”Most often, this happens because of Object Garbage Collection or a Missing Event Loop.
Fix 1: Preventing Garbage Collection (Scoping)
Section titled “Fix 1: Preventing Garbage Collection (Scoping)”If you create your window inside a function and don’t assign it to a persistent variable (like self or a global), Python deletes the window object as soon as the function finishes—often before the screen can even refresh.
# Illustrative example — Python 3.10+, PyQt6import sysfrom PyQt6.QtWidgets import QApplication, QMainWindow
def create_window(): # BAD: This is a local variable. # When create_window() ends, 'window' is deleted. window = QMainWindow() window.setWindowTitle("This won't stay open") window.show()
app = QApplication(sys.argv)create_window()sys.exit(app.exec())The Fix: Return the window object or store it in a class.
# Fixed versiondef create_window(): window = QMainWindow() window.setWindowTitle("This will stay open") window.show() return window # Keep the reference alive
app = QApplication(sys.argv)main_win = create_window() # Reference held heresys.exit(app.exec())Fix 2: Starting the Event Loop
Section titled “Fix 2: Starting the Event Loop”PyQt is an event-driven framework. If you call .show() but don’t start the executive loop, the window is “shown” in memory, but the application doesn’t have the “brain” active to actually draw the pixels or respond to clicks.
# Illustrative example — Python 3.11, PyQt5/6app = QApplication(sys.argv)window = QMainWindow()window.show()
# If you stop here, the script finishes (or hangs) and the window closes immediately.# You MUST start the event loop:sys.exit(app.exec())Detailed Explanation: Why is it “Stuck”?
Section titled “Detailed Explanation: Why is it “Stuck”?”When a developer says their app is “stuck in a loop,” they are usually describing one of two technical states:
- The Main Thread is Blocked: If you have a
while True:or a longtime.sleep()in your script, PyQt’s event loop cannot run. PyQt needs the main thread to process “paint events.” If you block the thread, the window will appear as a “Not Responding” white box or won’t appear at all. - The Event Loop is Running, but Empty: If
app.exec()is called but all your windows have been garbage collected (as shown in Fix 1), the event loop is running perfectly fine, but it has no windows to manage. It “hangs” because it’s waiting for events that will never come from a window that no longer exists.
Edge Case: The “Ghost” Process in IDEs
Section titled “Edge Case: The “Ghost” Process in IDEs”If you are using certain IDEs (like older versions of Spyder or Jupyter), they might already be running a QApplication instance in the background. If your script tries to create a second QApplication, it can collide and hang.
The Fix (Safe Initialization):
# Illustrative example — verify in your environmentapp = QApplication.instance() # Check if an instance already existsif not app: app = QApplication(sys.argv)Troubleshooting Decision Tree
Section titled “Troubleshooting Decision Tree”| Symptom | Likely Cause | Solution |
|---|---|---|
| Script exits immediately. | Missing app.exec(). |
Add sys.exit(app.exec()) at the end. |
| Window flashes for a millisecond then vanishes. | Variable Scope/GC. | Assign the window to a global variable or self.window. |
| Window is a white box (Not Responding). | Blocking the Main Thread. | Move heavy logic to QThread or use QTimer. |
| No window, but terminal is locked. | All windows closed/deleted. | Ensure at least one window object exists before app.exec(). |
Follow-up Questions
Section titled “Follow-up Questions”1. What is the difference between exec() and exec_()?
Section titled “1. What is the difference between exec() and exec_()?”In Python 2, exec was a reserved keyword, so PyQt used exec_() to avoid syntax errors. In Python 3, exec is no longer reserved in this context. If you are using PyQt6, use exec(). If you are using PyQt5, you can use either, but exec_() is more common for backward compatibility.
2. How do I run a “loop” without freezing the window?
Section titled “2. How do I run a “loop” without freezing the window?”Never use while True or time.sleep() in the main thread of a GUI app. Instead:
- Use
QTimer: To run a function every X milliseconds. - Use
QThread: For heavy computations (like API calls or file processing) so the UI remains responsive.
3. Why does my window only show up when I run it in the terminal?
Section titled “3. Why does my window only show up when I run it in the terminal?”Some IDEs have “Run in external terminal” vs “Run in internal console” settings. Internal consoles often struggle with GUI event loops. If your code is correct but doesn’t show a window, try running it directly via python your_script.py in your OS command prompt to rule out IDE interference.