This article is part 3 of 8 in the series Python PySide/PyQt Tutorial

Last Updated: Thursday 12th December 2013

In the last installment, we looked at some of the functionality provided to all QWidget-descended Qt widgets, and we looked at one specific widget, the QLabel, in more depth. We also worked our way up to an example that illustrated the structure of a simple Python/Qt application. Thus far, however, we're not able to do anything that's very satisfying for our users; our applications can show text to them. You might call them monologue boxes. We need some ways to allow the user to interact with our programs, to turn our monologue into a dialogue. Qt offers a wealth of interactive widgets, and we're going to consider a few of the simpler ones here; we'll use them to explore how to lay out widgets on a form. In the next installment, we're going to learn how to make our application respond to user interaction using slots and signals; this time, we will discuss how to create the widgets and lay them out on a form, including a simple example that uses what we've learned.

Interactive Widgets

Python/Qt makes it easy to allow user interaction; it has a very simple, sensible set of widgets, and they are easy to connect to your application's logic. Let's look at a few.

Buttons

One of the simplest ways to allow users to interact with an application is to let them click a button. Unsurprisingly, Qt has those, called a QPushButton. The QPushButton constructor has three valid signatures:

The parent argument is a QWidget, text is a Python string or unicode, and icon is a QIcon. To create a button owned by some-form with the caption "Go", we would do:

If we want a keyboard shortcut for our button, say Alt-G, we can do that by adding an ampersand before the 'G' in 'Go':

The 'G' will also be underlined on most platforms.

There are a few other things you can do with buttons. Taking our go_button above, you can make it the form's default button by calling:

You can make it flat:

Passing False to either method has the opposite effect. A button can raise a popup menu when it is clicked; to do this, pass a QMenu object to the button's setMenu method. (We'll investigate menus in a future installment.)

Textboxes

Qt's textbox control is called QLineEdit; it allows the user to enter and/or edit a single line of plain text. It has two constructor signatures:

The only difference between the two is that the second sets the text contained in the QLineEdit to text. The QLineEdit object has a great number of methods, but we'll concern ourselves with some of the most basic. You can retrieve its text with the text() method, set it with setText(text), and set the maximum number of characters that can be entered with setMaxLength(chars). It can be made read-only with setReadOnly(True), and placeholder text can be added using (unsurprisingly) setPlaceholderText(text). A QLineEdit has more advanced properties: you can set validators and input masks, handle selections and undo history, and more; we'll get to that later on.

Comboboxes

The QComboBox widget is used to present the user with a number of textual or text/icon choices, of which they must select only one. (For multiple selections, see QListView and QListWidget — we're getting there.) Its constructor receives only a parent:

It's that simple to construct, but it doesn't have anything in it yet. You can add items in one of several ways. If all your items are textual, you can use addItems(texts), where texts is a list of string items. To add items individually, you can use addItem, which has two valid signatures:

where icon is a QIcon, text is a unicode object, and userData is any object. You can insert items using insertItem:

or, if all your items are textual, insertItems(texts). The QComboBox is a flexible widget, and much more can be done with it, but this will get you started.

Example Application: Overview

Next, we'll learn how to combine widgets into a form layout — but before we can do that, let's briefly look at this installment's example application.

 PySide/PyQt Example App

As you can see, it's a very simple application. The user can select a salutation and enter the name of the person (or other entity) they want to greet, and when they click "Build Greeting", the greeting will be shown on a label on the form. It is this form that we'll be laying out in the next section.

Layout Management

PySide and PyQt have two available approaches to layout management: absolute positioning, in which the developer must specify the position and size of each widget, and use of layout containers, which fit widgets into a form in one of many arrangements and handle size and position automatically. We'll construct an interface for the example application described above using both methods.

Absolute Positioning

To set the physical position of a widget in a window, you use the widget's move(x, y) method; x and y are the horizontal and vertical distance, respectively, from the top left corner of the form to the top left corner of the widget. A Here is our form, created using absolute positioning:

Needless to say, that could get cumbersome in a serious application. It also fails to respond well to resizing; the labels just sit there in their assigned locations. Not only that, but imagine if a user with visual impairment has their fonts set extra-large; the fixed positions you set for the controls will no longer be appropriate.

Layout Containers

For all those reasons, layout containers are much more frequently used than absolute positioning; they are more flexible, they take the load of calculating exact positions off the programmer, and they can adjust layout to fit GUI guidelines of different platforms, such as Windows, GTK+, KDE, and Mac OS X, as well as different users' preferences. There are five main layout containers, each of which descends from QLayout:

  • QHBoxLayout
  • QVBoxLayout
  • QGridLayout
  • QStackedLayout
  • QFormLayout

Each has a different purpose. In a nutshell, the QHBoxLayout and QVBoxLayout arrange widgets one after another horizontally and vertically, respectively; the QGridLayout arranges them in a tabular layout of any size; the QStackedLayout arranges them one on top of each other, allowing them to come to the fore as needed; and the QFormLayout is a specialized grid arrangement with two columns that provides special methods for arranging common forms with labels in the first column and associated controls in the second. These layouts are useful enough on their own, but your layout options are not so limited as that: you can also nest layouts within other layouts to create more complex and flexible patterns for your user interface. For right now, we'll consider the vertical and horizontal box layouts and the QFormLayout.

QVBoxLayout and QHBoxLayout

The box layouts are fairly straightforward. To use one as a top-level layout, you simply create the layout — its constructor requires no arguments — and add widgets to it using the aptly-named addWidget method. Then, you set it as the layout for the window it belongs to. For example:

A QHBoxLayout can be used identically, though it is much less common as a top-level layout. It is most common as a sub-layout. To add a sub-layout to another layout, use the layout's addLayout method, e.g.:

Box layouts have another valuable and frequently-used method: addStretch. A common layout has a number of controls located statically at one end of a box layout, and some at the other end, with flexible empty space in between. To do this, add the widgets at the beginning of the box, add a stretch with a stretch factor greater than zero, i.e. layout.addStretch(1); then add the rest of the widgets.

QFormLayout

The QFormLayout is much like a QVBoxLayout, except that each row can easily be divided into two columns without creating nested layouts. This is done using the form layout's addRow method, which is copiously overloaded. The single-argument versions:

Add the widget or layout across the end of the entire QFormLayout. The double-argument versions:

Add the initial element in the first column as a "label", for the second, in the second column. The unicode arguments are used as the text of a QLabel; the QWidget can be any widget.

Box Layout Example

Now that we have a basic idea how to create interactive widgets and arrange them in more flexible layouts, let's recreate our interface in a simpler, more flexible way. Our window's main layout will be a QVBoxLayout, with two sub-layouts, a QFormLayout that contains all the labeled controls, and a QHBoxLayout that manages the position of the button at the bottom right. We'll use addStretch to separate the QFormLayout from the QHBoxLayout, and to push the button to the far right of the QHBoxLayout. You may notice that much of the code below is unchanged from the absolute-positioning example; there is no difference, for example, in how the individual controls are created.

Note especially how much easier this is on the programmer. While the code is no shorter — not every convenience leads to less typing, and creating and nesting the layouts has some code overhead — the mental effort is much less. The developer simply needs to arrive at a combination of layouts that produces the desired effect, and create them; the controls can be created and modified in isolation, with little consideration of their effect on the layout of other controls. Next installment, we'll take the interface we created in this example and make it actually do something.

  • Pingback: PySide/PyQt Tutorial: Using Built-In Signals and Slots - Python Central

  • Chris

    For this ‘tutorial’ to work, you’ll have to import the needed modules first in the nameSpace used. Which in this example is:
    import sys
    from PySide.QtCore import *
    from PySide.QtGui import *

    As a side not, if you happen to not work by your own anymore but with other people’s code, and they with yours – if you do stuff like import *, people might want to smack you in the face… so don’t get used to it at all if you can.

    From the Zen – “Explicit is better than implicit.”
    http://www.python.org/dev/peps/pep-0020/

  • Geomath Ahmet

    cool

  • Davood Wadi

    Jason, I’ve got to tell you this is not only the best but also the only Python Gui tutorial for beginners out there.
    Thank you so much for spending this much time on these tutorials.

    Keep on the nice work.