Creating additional windows
Opening new windows for your application

Getting started with PySide course

In an earlier tutorial we've already covered how to open dialog windows. These are special windows which (by default) grab the focus of the user, and run their own event loop, effectively blocking the execution of the rest of your app.

However, quite often you will want to open a second window in an application, without interrupting the main window -- for example, to show the output of some long-running process, or display graphs or other visualizations. Alternatively, you may want to create an application that allows you to work on multiple documents at once, in their own windows.

It's relatively straightforward to open new windows but there are a few things to keep in mind to make sure they work well. In this tutorial we'll step through how to create a new window, and how to show and hide external windows on demand.

Creating a new window

In Qt any widget without a parent is a window. This means, to show a new window you just need to create a new instance of a widget. This can be any widget type (technically any subclass of QWidget) including another QMainWindow if you prefer.

There is no restriction on the number of QMainWindow instances you can have. If you need toolbars or menus on your second window you will have to use a QMainWindow to achieve this. This can get confusing for users however, so make sure it's necessary.

As with your main window, creating a window is not sufficient, you must also show it.

python
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window")
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        w = AnotherWindow()
        w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

A main window with a button to launch a child window, A main window with a button to launch a child window,

If you run this, you'll see the main window. Clicking the button may show the second window, but if you see it it will only be visible for a fraction of a second. What's happening?

python
    def show_new_window(self, checked):
        w = AnotherWindow()
        w.show()

Inside this method, we are creating our window (widget) object, storing it in the variable w and showing it. However, once we leave the method we no longer have a reference to the w variable (it is a local variable) and so it will be cleaned up – and the window destroyed. To fix this we need to keep a reference to the window somewhere, for example on the self object.

python
    def show_new_window(self, checked):
        self.w = AnotherWindow()
        self.w.show()

Now, when you click the button to show the new window, it will persist.

However, what happens if you click the button again? The window will be re-created! This new window will replace the old in the self.w variable, and – because there is now no reference to it – the previous window will be destroyed.

You can see this in action if you change the window definition to show a random number in the label each time it is created.

python
from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)

The __init__ block is only run when creating the window. If you keep clicking the button the number will change, showing that the window is being re-created.

One solution is to simply check whether the window has already being created before creating it. The example below shows this in action.

python
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = None  # No external window yet.
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        if self.w is None:
            self.w = AnotherWindow()
        self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

Child window with a label randomly generated on creation. Child window with a label randomly generated on creation.

Using the button you can pop up the window, and use the window controls to close it. If you click the button again, the same window will re-appear.

This approach is fine for windows that you create temporarily – for example if you want to pop up a window to show a particular plot, or log output. However, for many applications you have a number of standard windows that you want to be able to show/hide them on demand.

In the next part we'll look at how to work with these types of windows.

Toggling a window

Often you'll want to toggle the display of a window using an action on a toolbar or in a menu. As we previously saw, if no reference to a window is kept, it will be discarded (and closed). We can use this behaviour to close a window, replacing the show_new_window method from the previous example with –

python
    def show_new_window(self, checked):
        if self.w is None:
            self.w = AnotherWindow()
            self.w.show()

        else:
            self.w = None  # Discard reference, close window.

By setting self.w to None the reference to the window will be lost, and the window will close.

If we set it to any other value that None the window will still close, but the if self.w is None test will not pass the next time we click the button and so we will not be able to recreate a window.

This will only work if you have not kept a reference to this window somewhere else. To make sure the window closes regardless, you may want to explicitly call .close() on it. The full example is shown below.

python
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = None  # No external window yet.
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        if self.w is None:
            self.w = AnotherWindow()
            self.w.show()

        else:
            self.w.close()  # Close window.
            self.w = None  # Discard reference.


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

Create GUI Applications with Python & Qt5
The easy way to create desktop applications

The complete guide to building GUI applications with PySide2. From the basics of creating a desktop window to the key features you need to build real apps.

Downloadable ebook (PDF, ePub) & Complete Source code

To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount with the code [[ couponCode ]] — Enjoy!

For [[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount with the code [[ couponCode ]] — Enjoy!

Persistent windows

So far we've looked at how to create new windows on demand. However, sometimes you have a number of standard application windows. In this case rather than create the windows when you want to show them, it can often make more sense to create them at start-up, then use .show() to display them when needed.

In the following example we create our external window in the __init__ block for the main window, and then our show_new_window method simply calls self.w.show() to display it.

python
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

If you run this, clicking on the button will show the window as before. However, note that the window is only created once and calling .show() on an already visible window has no effect.

Showing & hiding persistent windows

Once you have created a persistent window you can show and hide it without recreating it. Once hidden the window still exists, but will not be visible and accept mouse/other input. However you can continue to call methods on the window and update it's state -- including changing it's appearance. Once re-shown any changes will be visible.

Below we update our main window to create a toggle_window method which checks, using .isVisible() to see if the window is currently visible. If it is not, it is shown using .show() , if it is already visible we hide it with .hide().

python
class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.toggle_window)
        self.setCentralWidget(self.button)

    def toggle_window(self, checked):
        if self.w.isVisible():
            self.w.hide()

        else:
            self.w.show()

The complete working example of this persistent window and toggling the show/hide state is shown below.

python
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.toggle_window)
        self.setCentralWidget(self.button)

    def toggle_window(self, checked):
        if self.w.isVisible():
            self.w.hide()

        else:
            self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

Note that, again, the window is only created once -- the window's __init__ block is not re-run (so the number in the label does not change) each time the window is re-shown.

Multiple windows

You can use the same principle for creating multiple windows -- as long as you keep a reference to the window, things will work as expected. The simplest approach is to create a separate method to toggle the display of each of the windows.

python
import sys
from random import randint

from PySide2.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent,
    it will appear as a free-floating window.
    """

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0, 100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.window1 = AnotherWindow()
        self.window2 = AnotherWindow()

        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(self.toggle_window1)
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(self.toggle_window2)
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def toggle_window1(self, checked):
        if self.window1.isVisible():
            self.window1.hide()

        else:
            self.window1.show()

    def toggle_window2(self, checked):
        if self.window2.isVisible():
            self.window2.hide()

        else:
            self.window2.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

A mainwindow with two child windows. A mainwindow with two child windows.

However, you can also create a generic method which handles toggling for all windows -- see transmitting extra data with Qt signals for a detailed explanation of how this works. The example below shows that in action, using a lambda function to intercept the signal from each button and pass through the appropriate window. We can also discard the checked value since we aren't using it.

python
import sys
from random import randint

from PySide2.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent,
    it will appear as a free-floating window.
    """

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0, 100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.window1 = AnotherWindow()
        self.window2 = AnotherWindow()

        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(
            lambda checked: self.toggle_window(self.window1)
        )
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(
            lambda checked: self.toggle_window(self.window2)
        )
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def toggle_window(self, window):
        if window.isVisible():
            window.hide()

        else:
            window.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
Complete Mark As Complete Finish

Continue reading

QComboBox  pyside

The QComboBox is a simple widget for presenting a list of options to your users in PyQt, taking up the minimum amount of screen space. The widget can have a single selected item, which is displayed in the widget area. When selected a QComboBox pops out a list of possible values from which you can select. A QComboBox can also - optionally - be made editable, so your users can enter their own values onto the list of possible values. QComboBox in its closed and open state Populating a QComboBox Items can be added or inserted to a QComboBox, where adding appends the item to the end of the current list, while insert inserts them in a specific index in the existing list. There are convenience methods for adding multiple items to a combobox and also for adding icons to the list. The following example will demonstrate each of these using a series of comboboxes. python from PyQt5.QtWidgets import QComboBox, QMainWindow, QApplication, QWidget, QVBoxLayout from PyQt5.QtGui import QIcon import sys class MainWindow(QMainWindow): def __init__(self): super().__init__() combobox1 = QComboBox() combobox1.addItem('One') combobox1.addItem('Two') combobox1.addItem('Three') combobox1.addItem('Four') combobox2 = QComboBox() combobox2.addItems(['One', 'Two', 'Three', 'Four']) combobox3 = QComboBox() combobox3.addItems(['One', 'Two', 'Three', 'Four']) combobox3.insertItem(2, 'Hello!') combobox4 = QComboBox() combobox4.addItems(['One', 'Two', 'Three', 'Four']) combobox4.insertItems(2, ['Hello!', 'again']) combobox5 = QComboBox() icon_penguin = QIcon('animal-penguin.png') icon_monkey = QIcon('animal-monkey.png') icon_bauble = QIcon('bauble.png') combobox5.addItem(icon_penguin, 'Linux') combobox5.addItem(icon_monkey, 'Monkeyix') combobox5.insertItem(1, icon_bauble, 'Baublix') layout = QVBoxLayout() layout.addWidget(combobox1) layout.addWidget(combobox2) layout.addWidget(combobox3) layout.addWidget(combobox4) layout.addWidget(combobox5) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def current_text_changed(self, s): print("Current text: ", s) app = QApplication(sys.argv) w = MainWindow() w.show() app.exec_() Run the example and compare the result with each series of add and insert steps. Adding items to a QComboBox You can replace the text at a specific index in the list by calling .setItemText() for example. python widget.setItemText(3, 'new text') Finally, you can clear a QComboBox -- removing all items in it -- by calling .clear(). python widget.clear() QComboBox signals The QComboBox emits a number of signals on state changes. When the currently selected item changes, the widget emits .currentIndexChanged() and .currentTextChanged() signals. The first receives the index of the selected entry while the second receives the text of that item. There is a further signal .activated() which is emitted for user-selections whether the index is changed or not: selecting the same entry again will still emit the signal. Highlighting an entry in the QComboBox popup list will emit the .highlighted() signal. The following example demonstrates these signals in action. python from PyQt5.QtWidgets import QComboBox, QMainWindow, QApplication import sys class MainWindow(QMainWindow): def __init__(self): super().__init__() combobox = QComboBox() combobox.addItems(['One', 'Two', 'Three', 'Four']) # Connect signals to the methods. combobox.activated.connect(self.activated) combobox.currentTextChanged.connect(self.text_changed) combobox.currentIndexChanged.connect(self.index_changed) self.setCentralWidget(combobox) def activated(Self, index): print("Activated index:", index) def text_changed(self, s): print("Text changed:", s) def index_changed(self, index): print("Index changed", index) app = QApplication(sys.argv) w = MainWindow() w.show() app.exec_() If you run the example the signals emitted as you interact with the QComboBox will be shown in the console, giving output like that shown below. Note that when re-selecting the same entry, only the .activated() signal is emitted. python Index changed 1 Text changed: Two Activated index: 1 Index changed 2 Text changed: Three Activated index: 2 Index changed 3 Text changed: Four Activated index: 3 Activated index: 3 Activated index: 3 Getting the current state In addition to the signals, QComboBox has a few methods for getting the current state at any time. For example, you can use .currentIndex() to get the index of the currently selected item in the combobox, or use .currentText() to get the text. With the index you can also look up the text of a specific item using .itemText(). Finally, you can use .count() to get the total number of items in the combobox list. In the example below, we use the activated signal we've already seen to hook up a number of methods which get information about the combobox and display this in the console. As you select any item in the combobox every method will run printing a message to the console. python from PyQt5.QtWidgets import QComboBox, QMainWindow, QApplication import sys class MainWindow(QMainWindow): def __init__(self): super().__init__() # Keep a reference to combobox on self, so we can access it in our methods. self.combobox = QComboBox() self.combobox.addItems(['One', 'Two', 'Three', 'Four']) # Connect signal to our methods. self.combobox.activated.connect(self.check_index) self.combobox.activated.connect(self.current_text) self.combobox.activated.connect(self.current_text_via_index) self.combobox.activated.connect(self.current_count) self.setCentralWidget(self.combobox) def check_index(self, index): cindex = self.combobox.currentIndex() print(f"Index signal: {index}, currentIndex {cindex}") def current_text(self, _): # We receive the index, but don't use it. ctext = self.combobox.currentText() print("Current text", ctext) def current_text_via_index(self, index): ctext = self.combobox.itemText(index) # Get the text at index. print("Current itemText", ctext) def current_count(self, index): count = self.combobox.count() print(f"Index {index+1}/{count}") app = QApplication(sys.argv) w = MainWindow() w.show() app.exec_() Running this and selecting different items in the combobox will show the outputs. python Current text Two Current itemText Two Index 1/4 Index signal: 2, currentIndex 2 Current text Three Current itemText Three Index 2/4 Index signal: 3, currentIndex 3 Current text Four Current itemText Four Index 3/4 Editable comboboxes You can set a QComboBox to be editable allowing the user to type enter the values not currently in the list. Entered values can be added to the list, or just used as a value which is not inserted. To enable editing you can call .setEditable(True) on the widget, while control of how inserts operate is handled through the insert policy. This is set by calling .setInsertPolicy() passing one of the following flags: Flag Value Behavior QComboBox.NoInsert 0 No insert QComboBox.InsertAtTop 1 Insert as first item QComboBox.InsertAtCurrent 2 Replace currently selected item QComboBox.InsertAtBottom 3 Insert after last item QComboBox.InsertAfterCurrent 4 Insert after current item QComboBox.InsertBeforeCurrent 5 Insert before current item QComboBox.InsertAlphabetically 6 Insert in alphabetical order For PyQt6 use the fully-qualified flag name, i.e. QComboBox.InsertPolicy.NoInsert For example, to insert new values alphabetically, you would use. python widget.setInsertPolicy(QComboBox.InsertAlphabetically) If the combobox is set to be editable, but QComboBox.NoInsert is set as the insert policy, users will be able to enter their own custom values, but they will not be added to the list. In the following example we have two QComboBox widgets: one which is our editable box and the second which controls the insert policy of the first. Note that since the values of the flags (see above) are just numbers from 0 to 6, we can use the index to determine the flag. python from PyQt5.QtWidgets import QComboBox, QMainWindow, QApplication, QWidget, QVBoxLayout import sys class MainWindow(QMainWindow): def __init__(self): super().__init__() # Keep a reference to combobox on self, so we can access it in our methods. self.combobox = QComboBox() self.combobox.addItems(['One', 'Two', 'Three', 'Four']) self.combobox.setEditable(True) self.combobox.currentTextChanged.connect(self.current_text_changed) # Insert policies self.insertpolicy = QComboBox() self.insertpolicy.addItems([ 'NoInsert', 'InsertAtTop', 'InsertAtCurrent', 'InsertAtBottom', 'InsertAfterCurrent', 'InsertBeforeCUrrent', 'InsertAlphabetically' ]) # The index in the insertpolicy combobox (0-6) is the correct flag value to set # to enable that insert policy. self.insertpolicy.currentIndexChanged.connect(self.combobox.setInsertPolicy) layout = QVBoxLayout() layout.addWidget(self.combobox) layout.addWidget(self.insertpolicy) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def current_text_changed(self, s): print("Current text: ", s) app = QApplication(sys.argv) w = MainWindow() w.show() app.exec_() If you run this example you'll notice you can now type into the (top) editable combobox and submit them by pressing Enter. The .currentTextChanged() signal will be emitted as you type into the field. python Current text: w Current text: wq Current text: wqe Current text: wqew Current text: wqeww Current text: wqewwq Current text: wqewwqe By default the insert policy is set to QComboBox.NoInsert so entered values will not be added, just set as the value of the combobox. By selecting the insert policy in the second combobox you can change this behavior, having new entries added to the list. Editing a QComboBox with insert policy When you allow users to add values to the list, you may wish to constrain the maximum number of entries. This can be done using .setMaxCount, e.g. python widget.setMaxCount(10) More