Plotting with Matplotlib
Create PySide plots with the popular Python plotting library

Graphics and Plotting course

In a previous tutorial we covered plotting in PySide using PyQtGraph. PyQtGraph uses the Qt vector-based QGraphicsScene to draw plots and provides a great interface for interactive and high performance plotting.

However, there is another plotting library for Python which is used far more widely, and which offers a richer assortment of plots — Matplotlib. If you're migrating an existing data analysis tool to a Python GUI, or if you simply want to have access to the array of plot abilities that Matplotlib offers, then you'll want to know how to include Matplotlib plots within your application.

In this tutorial we'll cover how to embed Matplotlib plots in your PySide applications

Matplotlib doesn't yet support PySide6, once it does this tutorial will be updated.

Many other Python libraries — such as seaborn and pandas— make use of the Matplotlib backend for plotting. These plots can be embedded in PySide in the same way shown here, and the reference to the axes passed when plotting. There is a pandas example at the end of this tutorial.

Installing Matplotlib

The following examples assume you have Matplotlib installed. If not you can install it as normal using Pip, with the following —

bash
pip install matplotlib

A simple example

The following minimal example sets up a Matplotlib canvas FigureCanvasQTAgg which creates the Figure and adds a single set of axes to it. This canvas object is also a QWidget and so can be embedded straight into an application as any other Qt widget.

python
import sys
import matplotlib

matplotlib.use('Qt5Agg')

from PySide2.QtWidgets import QMainWindow, QApplication

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
        self.setCentralWidget(sc)

        self.show()


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

In this case we're adding our MplCanvas widget as the central widget on the window with .setCentralWidget(). This means it will take up the entirety of the window and resize together with it. The plotted data [0,1,2,3,4], [10,1,20,3,40] is provided as two lists of numbers (x and y respectively) as required by the .plot method.

Basic plot with embedded Matplotlib Basic plot with embedded Matplotlib

Plot controls

Plots from Matplotlib displayed in PySide are actually rendered as simple (bitmap) images by the Agg backend. The FigureCanvasQTAgg class wraps this backend and displays the resulting image on a Qt widget. The effect of this architecture is that Qt is unaware of the positions of lines and other plot elements — only the x, y coordinates of any clicks and mouse movements over the widget.

However, support for handling Qt mouse events and transforming them into interactions on the plot is built into Matplotlib. This can be controlled through a custom toolbar which can be added to your applications alongside the plot. In this section we'll look at adding these controls so we can zoom, pan and get data from embedded Matplotlib plots.

The complete code, importing the toolbar widget NavigationToolbar2QT and adding it to the interface within a QVBoxLayout, is shown below —

python
import sys
import matplotlib
matplotlib.use('Qt5Agg')

from PySide2.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QApplication

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self.show()


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

We'll step through the changes.

First we import the toolbar widget from matplotlib.backends.backend_qt5agg.NavigationToolbar2QT renaming it with the simpler name NavigationToolbar. We create an instance of the toolbar by calling NavigationToolbar with two parameters, first the canvas object sc and then the parent for the toolbar, in this case our MainWindow object self. Passing in the canvas links the created toolbar to it, allowing it to be controlled. The resulting toolbar object is stored in the variable toolbar.

We need to add two widgets to the window, one above the other, so we use a QVBoxLayout. First we add our toolbar widget toolbar and then the canvas widget sc to this layout. Finally, we set this layout onto our simple widget layout container which is set as the central widget for the window.

Running the above code will produce the following window layout, showing the plot at the bottom and the controls on top as a toolbar.

Matplotlib plot with Toolbar Matplotlib plot with Toolbar

The buttons provided by NavigationToolbar2QT allow the following actions —

  • Home, Back/Forward, Pan & Zoom which are used to navigate through the plots. The Back/Forward buttons can step backwards and forwards through navigation steps, for example zooming in and then clicking Back will return to the previous zoom. Home returns to the initial state of the plot.
  • Plot margin/position configuration which can adjust the plot within the window.
  • Axis/curve style editor, where you can modify plot titles and axes scales, along with setting plot line colours and line styles. The colour selection uses the platform-default colour picker, allowing any available colours to be selected.
  • Save, to save the resulting figure as an image (all Matplotlib supported formats).

A few of these configuration settings are shown below.

Matplotlib figure options Matplotlib figure options

Matplotlib curves figure options Matplotlib curves figure options

For more information on navigating and configuring Matplotlib plots, take a look at the official Matplotlib toolbar documentation.

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

My complete guide, updated for 2021 & PySide6. Everything you need 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!

Updating plots

Quite often in applications you'll want to update the data shown in plots, whether in response to input from the user or updated data from an API. There are two ways to update plots in Matplotlib, either

  1. clearing and redrawing the canvas (simpler, but slower) or,
  2. by keeping a reference to the plotted line and updating the data.

If performance is important to your app it is recommended you do the latter, but the first is simpler.

Clear and redraw

We start with the simple clear-and-redraw method first below —

python
import sys
import random
import matplotlib
matplotlib.use('Qt5Agg')

from PySide2.QtWidgets import QMainWindow, QApplication
from PySide2.QtCore import QTimer

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]
        self.canvas.axes.cla()  # Clear the canvas.
        self.canvas.axes.plot(self.xdata, self.ydata, 'r')
        # Trigger the canvas to update and redraw.
        self.canvas.draw()


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

In this example we've moved the plotting to a update_plot method to keep it self-contained. In this method we take our ydata array and drop off the first value with [1:] then append a new random integer between 0 and 10. This has the effect of scrolling the data to the left.

To redraw we simply call axes.cla() to clear the axes (the entire canvas) and the axes.plot(…) to re-plot the data, including the updated values. The resulting canvas is then redrawn to the widget by calling canvas.draw().

The update_plot method is called every 100 msec using a QTimer. The clear-and-refresh method is fast enough to keep a plot updated at this rate, but as we'll see shortly, falters as the speed increases.

In-place redraw

The changes required to update the plotted lines in-place are fairly minimal, requiring only an addition variable to store and retrieve the reference to the plotted line. The updated MainWindow code is shown below.

python
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]

        # We need to store a reference to the plotted line
        # somewhere, so we can apply the new data to it.
        self._plot_ref = None
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]

        # Note: we no longer need to clear the axis.
        if self._plot_ref is None:
            # First time we have no plot reference, so do a normal plot.
            # .plot returns a list of line <reference>s, as we're
            # only getting one we can take the first element.
            plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
            self._plot_ref = plot_refs[0]
        else:
            # We have a reference, we can use it to update the data for that line.
            self._plot_ref.set_ydata(self.ydata)

        # Trigger the canvas to update and redraw.
        self.canvas.draw()

First, we need a variable to hold a reference to the plotted line we want to update, which here we're calling _plot_ref. We initialize self._plot_ref with None so we can check its value later to determine if the line has already been drawn — if the value is still None we have not yet drawn the line.

T> If you were drawing multiple lines you would probably want to use a list or dict data structure to store the multiple references and keep track of which is which.

Finally, we update the ydata data as we did before, rotating it to the left and appending a new random value. Then we either —

  1. if self._plotref is None (i.e. we have not yet drawn the line) draw the line and store the reference in self._plot_ref, or
  2. update the line in place by calling self._plot_ref.set_ydata(self.ydata)

We obtain a reference to the plotted when calling .plot. However .plot returns a list (to support cases where a single .plot call can draw more than one line). In our case we're only plotting a single line, so we simply want the first element in that list – a single Line2D object. To get this single value into our variable we can assign to a temporary variable plot_refs and then assign the first element to our self._plot_ref variable.

python
plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
self._plot_ref = plot_refs[0]

You could also use tuple-unpacking, picking off the first (and only) element in the list with —

python
self._plot_ref, = self.canvas.axes.plot(self.xdata, self.ydata, 'r')

If you run the resulting code, there will be no noticeable difference in performance between this and the previous method at this speed. However if you attempt to update the plot faster (e.g. down to every 10 msec) you'll start to notice that clearing the plot and re-drawing takes longer, and the updates do not keep up with the timer. We can compare the two versions below —

Both using 100 msec timer, clear-and-redraw on the left, update-in-place on the right.

Both using 10 msec timer, clear-and-redraw on the left, update-in-place on the right.

Whether this performance difference is enough to matter in your application depends on what you're building, and should be weighed against the added complication of keeping and managing the references to plotted lines.

Embedding plots from Pandas

Pandas is a Python package focused on working with table (data frames) and series data structures, which is particularly useful for data analysis workflows. It comes with built-in support for plotting with Matplotlib and here we'll take a quick look at how to embed these plots into PySide. With this you will be able to start building PySide data-analysis applications built around Pandas.

Pandas plotting functions are directly accessible from the DataFrame objects. The function signature is quite complex, giving a lot of options to control how the plots will be drawn.

python
DataFrame.plot(
    x=None, y=None, kind='line', ax=None, subplots=False,
    sharex=None, sharey=False, layout=None, figsize=None,
    use_index=True, title=None, grid=None, legend=True, style=None,
    logx=False, logy=False, loglog=False, xticks=None, yticks=None,
    xlim=None, ylim=None, rot=None, fontsize=None, colormap=None,
    table=False, yerr=None, xerr=None, secondary_y=False,
    sort_columns=False, **kwargs
)

The parameter we're most interested in is ax which allows us to pass in our own matplotlib.Axes instance on which Pandas will plot the DataFrame.

python
import sys
import matplotlib
matplotlib.use('Qt5Agg')

from PySide2.QtWidgets import QMainWindow, QApplication

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        self.setCentralWidget(sc)
        self.show()


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

The key step here is passing the canvas axes in when calling the plot method on the DataFrameon the line df.plot(ax=sc.axes). You can use this same pattern to update the plot any time, although bear in mind that Pandas clears and redraws the entire canvas, meaning that it is not ideal for high performance plotting.

The resulting plot generated through Pandas is shown below —

Pandas plot embedded in PySide Pandas plot embedded in PySide

Just as before, you can add the Matplotlib toolbar and control support to plots generated using Pandas, allowing you to zoom/pan and modify them live. The following code combines our earlier toolbar example with the Pandas example.

python
import sys
import matplotlib
matplotlib.use('Qt5Agg')

from PySide2.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QWidget

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()


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

Running this you should see the following window, showing a Pandas plot embedded in PySide alongside the Matplotlib toolbar.

Pandas plot with Matplotlib toolbar Pandas plot with Matplotlib toolbar

What's next

In this tutorial we looked at how you can embed Matplotlib plots in your PySide applications. Being able to use Matplotlib plots in your applications allows you to create custom data analysis and visualization tools from Python.

Matplotlib is a huge library and too big to cover in detail here. If you're not familiar with Matplotlib plotting and want to give it a try, take a look at the documentation and example plots to see what is possible. If you are familiar with it you should now be able to put those skills to work in your PySide apps!

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