Spring Basics: @Conditional Bean Wiring

Table of Contents

A Use Case for Conditional Bean Wiring

Suppose we want to enable caching for our web application. We are interested to find out whether this helps our application or whether the overhead will so big that it actually slows our application down. We decide we want to put the new functionality behind a feature toggle. We add a new property to our application called app.caching.enabled=false. When we are ready to enable the caching, we change the property’s value to true and we are in business. When the results disappoint, we can easily revert the property’s value to false.

@Conditional Introduction

The @Conditional annotation can be used on any type or method that declares a bean:

@Conditional(SomeCondition.class)
@Bean
public SomeBean someBean() {
    return new SomeBean();
}

@Conditional takes a mandatory class that implements the functional interface Condition. Condition defines a single method matches that returns a boolean. The method decides whether the bean should be loaded into the Spring context or whether it should be ignored:

public SomeCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        /* make decision based on context and metadata */
    }
}

A @Conditional @Configuration

When @Conditional is applied to a class which is also annotated with @Configuration, then all of the @Bean methods, @Import, @ComponentScan and other annotations will be subject to the condition. This means that when the condition evaluates to false, all of the configuration defined in that class will be ignored.

Since creating a cache carries costs with it (even an unused cache reserves space on the heap for its initial size), this is exactly what we want in our situation.

@Configuration
@Conditional(CacheCondition.class)
public class CacheConfig {

    /* ... define cache manager and caches here ... */

    public static CacheCondition implements ConfigurationCondition {
        public ConfigurationPhase getConfigurationPhase() {
            return ConfigurationPhase.PARSE_CONFIGURATION;
        }

        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return context.getEnvironment().getRequiredProperty("app.caching.enabled", Boolean.class);
        }
    }
}

Since we are dealing with a conditional configuration instead of a regular bean, the condition class implements ConfigurationCondition instead of Condition. The ConfigurationCondition makes us implement another method that specifies at which point the condition should be evaluated. Normally, this ConfigurationPhase is set to REGISTER_BEAN, which means it checks the condition when adding a regular bean. In this case, we want it set to PARSE_CONFIGURATION, which makes Spring evaluate the condition at the moment the configuration class is parsed. If the condition does not match at that moment, the @Configuration class will not be added to the context.

Dependencies

The only dependency is on spring-context:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.1.RELEASE</version>
</dependency>

Sample Project

A sample project can be found on GitLab.

Spring Basics: XML Setter Injection With Custom Method Names

In a previous blog post we ended up with the following XML-based configuration:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>

    <util:list id="myRunningStreaks">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running">
        <property name="streaks" ref="myRunningStreaks"/>
    </bean>

</beans>

The complete code can be found on GitLab.

To recap, we instantiate a bean of type LocalDate by invoking its now() static method. We inject this bean into two PositiveStreak beans using constructor injection. We reference the LocalDate bean by its id, startTime. Next, we create a bean that is a list of PositiveStreaks using the util namespace. We then inject this list into a Habit class called Running using setter injection.

For setter injection to work from XML, the setter method needs to follow Java bean conventions. This means that there should be setter method called set<PropertyName>, where the important part is that the method start with set. The name of the property itself can be something completely different from the field it is setting, Spring does not care. For example, the following would work without a hitch:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void setStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}
<bean class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks" ref="myRunningStreaks" />
</bean>

The only important thing is that the name attribute of the property element corresponds to the Property part of setProperty setter method in Java. To reiterate: The name of the field that is being set by the mutator method can be something completely different from the name attribute of the property element.

But sometimes we want to be creative with our method names if that makes the code more readable, or maybe we are dealing with code that we cannot change. If we end up with a mutator method that does not start with set, you are now in a position to appreciate that Spring will not find this method and throws a BeanCreationException.

Handling Mutators With a Non-Standard Names

On StackOverflow a solution was proposed that uses a MethodInvokingFactoryBean. But since we are invoking a method that does not return a result, the documentation recommends we use MethodInvokingBean instead.

Suppose we have the follow setter in place:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void replaceStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}

We could leverage a MethodInvokingBean like so:

<bean id="running" class="com.relentlesscoding.wirebeans.Running">

<bean id="caller" class="org.springframework.beans.factory.config.MethodInvokingBean">
    <property name="targetObject" ref="running"/>
    <property name="targetMethod" value="replaceStreaks"/>
    <property name="arguments" ref="myRunningStreaks"/>
</bean>

(Notice that we actually use setter injection on the MethodInvokingBean by specifying properties on it.) The targetObject attribute specifies on which instance a targetMethod should be invoked. We can specify our custom mutator name, and pass a reference to our arguments.

This is verbose, so think carefully before you violate the Java bean naming convention. Consider using a facade if the code is not under your control. Alternatively, Java-based Spring configuration with the @Autowired annotation will work on a method with any name.

Spring Basics: Wiring Beans with XML Configuration

If you have to work with legacy Spring applications, chances are you will have know how XML-based configuration works. Although Java configuration is preferred for new applications, sometimes you just don’t have a choice.

The Code

You can find the code from this blog post on GitLab.

Dependencies

The only dependency you need to get a Spring container running is spring-context. Add in Maven:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

Declaring Beans in XML

The following class will become a bean:

package com.relentlesscoding.wirebeans;

import java.util.List;

public class Running implements Habit {
    private final String name = "Running";
    private final String description = "Run 10 km every day";
    private List<Streak> streaks;

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public List<Streak> getStreaks() {
        return streaks;
    }

    public void setStreaks(List<Streak> streaks) {
        this.streaks = streaks;
    }

    public void addStreak(Streak streak) {
        streaks.add(streak);
    }
}

Create an XML file, give it any name (I would call it applicationContext.xml) and place it in your src/main/resources.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd ">

    <bean id="running" class="com.relentlesscoding.wirebeans.Running"/>
</beans>

This would wire a bean with the id running that is of type Running (which is a Habit). You can see why this is much more of a hassle to set up than using a simple @Component annotation on a class or a @Bean annotation in a @Configuration class. Those XML namespaces are nasty, but luckily most IDEs will help you with them.

Injecting Beans

To inject dependencies, we have two choices:

  • Constructor injection
  • Setter injection

Unlike with Java configuration, we cannot insert into fields when using XML-based configuration.

Constructor Injection

Suppose we want to insert one bean into another bean, for instance a HabitRepository that persists habits to the database into a HabitService. By using the <constructor-arg /> element and the ref property, we can accomplish this:

<bean id="habitRepository" class="com.relentlesscoding.wirebeans.HabitRepository" />

<bean id="habitService" class="com.relentlesscoding.wirebeans.HabitService">
    <constructor-arg ref="habitRepository" />
</bean>

If we wanted to pass more arguments to the HabitService constructor, we must keep on eye on the order: it must be the same as the order in which they are declared in the class.

To ease working with constructor arguments and as a way to curtail the verbosity of the XML configuration, Spring offers the c XML namespace to help wire beans without the need to create a sub-element <constructor-arg />:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="currentTime" class="java.time.LocalDate" factory-method="now" />

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:startTime-ref="currentTime" />

We define a bean of type java.time.LocalDate and we use the static factory method now() to get an instance of it. We then use the c namespace to pass it to the constructor of our PositiveStreak bean. c:startTime-ref="currentTime" should be read as: pass the reference to the bean with id currentTime to the constructor argument that has the name startTime.

So we can reference constructor arguments by name. We can also reference them by position. c:_0-ref="currentTime" would do the exact same thing. XML does not allow a digit as the first character of an attribute, so we have to use an underscore. If there is only a single argument to the constructor, we can even use the shorthand c:_-ref="currentTime". I would not want to promote this as readable, but it’s good to know it exists and might be used in the wild.

Read more about the c namespace here.

Setter Injection

To use setter injection with XML-based configuration, you use the <property> element. If you look back at the Running class above, you see it has a method setStreak that takes a List<Streak>:

<bean id="startTime" class="java.time.LocalDate" factory-method="now" />

<bean id="streak"
    class="com.relentlesscoding.wirebeans.PositiveStreak"
    c:_0-ref="startTime" />

<bean id="running" class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks">
        <list>
            <ref bean="streak" />
        </list>
    </property>
</bean>

The <property> element has an attribute name that refers to the field name of the bean being set. In the current case, class Running has a field named streaks. As a child element of <property> we define a list of Streak references.

For a list of literal String values, this would have looked like:

<property name="listOfStrings">
    <list>
        <value>string value 1</value>
        <value>string value 1</value>
        <value><null/></value>
    </list>
</property>

The list is wired with literal Strings and even a literal null.

Spring also provides the p namespace to make this more convenient (less verbose):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:_0-ref="startTime" />

    <bean id="running"
        class="com.relentlesscoding.wirebeans.Running"
        p:streaks-ref="streak" />
</beans>

The usage of the p namespace is a lot like that of the c namespace. In this case, p:streaks-ref="streak" tells spring to wire a property named streaks with the bean that is referenced by the id streak. Now, the property streaks takes a List. If we pass only a single element to that list, the current syntax works and Spring will happily insert the single reference to streak into a List for us and pass that to the setter method. If we want to pass more than one element in a list, however, we have to create the list separately first, and then pass the id of that reference to the p property:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>

    <util:list id="myRunningStreak">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running" p:streaks-ref="myRunningStreak"/>
</beans>

We need to add the util namespace and the location of the util schema definition to get this to work. You see that the XML becomes quite verbose the more you try to do with it. The util namespace allows us to create collections of literal values or beans. These collections can then be referenced by their id.

More information:

Taking the App for a Test Ride

package com.relentlesscoding.wirebeans;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class HabitTest {

    @Autowired
    Habit runningHabit;

    @Test
    public void runningHabitIsNotNull() {
        Assert.assertNotNull(runningHabit);
    }

    @Test
    public void runningHabitHasSingleStreak() {
        Assert.assertEquals(2, runningHabit.getStreaks().size());
    }
}

To run integration tests where the Spring context is available, you need the following dependency:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>test</scope>
</dependency>

spring-test contains the SpringRunner JUnit runner, and the @ContextConfiguration that will tell Spring where to look for the application context that contains the beans that need to be wired. In this case, we tell it to look at the applicationContext.xml that we put in src/main/resources, so we can reference it by looking at the root of our classpath with the attribute locations = "classpath:applicationContext.xml".

Always-on VPN and captive portals

I was attending a meetup that was taking place in a bar in Utrecht. The first thing you want to do is to make a connection to the internet and get started. The location used a captive portal, however. You know: you have the name of the wireless network (SSID) and the password, but when you try to open any web page, you are directed towards a login page where you have to accept the terms and conditions of whoever is operating the network.

But what if you use an always-on VPN? You cannot connect to the network, because your MAC and IP address are not whitelisted yet by the operator. And you cannot get to the login page, because you do not allow any traffic outside your VPN.

The captive portal page.
The captive portal page.

UFW

I use ufw (uncomplicated firewall) as my firewall of choice, mainly because the alternative, iptables, always looked too complicated and ufw served its purpose. The rules I have for ufw are currently:

# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), deny (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
Anywhere on wlp1s0         ALLOW IN    192.168.178.0/24

Anywhere                   ALLOW OUT   Anywhere on tun0-unrooted
192.168.178.0/24           ALLOW OUT   Anywhere on wlp1s0
1194                       ALLOW OUT   Anywhere on wlp1s0
Anywhere (v6)              ALLOW OUT   Anywhere (v6) on tun0-unrooted
1194 (v6)                  ALLOW OUT   Anywhere (v6) on wlp1s0

By default, I deny all incoming and outgoing connections. I only allow incoming connections from hosts on the same network. As for outgoing connections, I only allow them to other hosts over the LAN (to any port), and everywhere else only over port 1194 (the OpenVPN port). (wlp1s0 is the name of my wireless interface, tun0-unrooted of the VPN tunnel. These rules were inspired by this Arch Linux article).

So we need to allow some traffic outside of the VPN tunnel to accept the terms and conditions and to register our machine at the captive portal. The best thing to do would be to allow a single, trusted application to access this portal, one that would be used exclusively for this task. If you would allow you regular browser to bypass the VPN, it would send all kind of traffic over the untrusted network for the rest of the world to freely sniff around in (think add-ons, other browser tabs, automatic updates). So we would need a dedicated web browser for this task. I’m on Linux using Firefox as my default browser, so GNOME Web would be a good choice for this purpose. (Gnome Web was previously known as Epiphany, and is still available under that name on a lot of distributions) .

First, we need to determine what kind of traffic we want to allow. The application will need to have outbound access to ports 80 (HTTP) and 443 (HTTPS) for web traffic, and it will also need to be able to resolve domain names using DNS, so port 53 should also be opened.

UFW Profiles

However, it’s not that easy to allow one particular application access to the internet if you use UFW. When you look at the man page for UFW, you see you can specify “apps”. Apps (or application profiles) are basically just text files in INI-format that live in the /etc/ufw/applications.d/ folder.

To list all (predefined) profiles:

# ufw app list

To create a profile for our purposes, we put the following in a file called ufw-webbrowser:

[Epiphany]
title=Epiphany
description=Epiphany web browser
ports=80/tcp|443/tcp|53

The “ports” field is clarified in the man page:

The ‘ports’ field may specify a ‘|’-separated list of ports/protocols where the protocol is optional. A comma-separated list or a range (specified with ‘start:end’) may also be used to specify multiple ports, in which case the protocol is required.

In our case we allow TCP traffic over ports 80 (HTTP) and 443 (HTTPS) and both UDP and TCP traffic over 53 (DNS). We can now use this profile:

# ufw insert 1 allow out to any app Epiphany
Rule added
Rule added (v6)
# ufw status verbose
[...snip...]
To                         Action      From
--                         ------      ----
Anywhere on wlp1s0         ALLOW IN    192.168.178.0/24

80/tcp (Epiphany)          ALLOW OUT   Anywhere
443/tcp (Epiphany)         ALLOW OUT   Anywhere
53 (Epiphany)              ALLOW OUT   Anywhere
Anywhere                   ALLOW OUT   Anywhere on tun0-unrooted
192.168.178.0/24           ALLOW OUT   Anywhere on wlp1s0
1194                       ALLOW OUT   Anywhere on wlp1s0
80/tcp (Epiphany (v6))     ALLOW OUT   Anywhere (v6)
443/tcp (Epiphany (v6))    ALLOW OUT   Anywhere (v6)
53 (Epiphany (v6))         ALLOW OUT   Anywhere (v6)
Anywhere (v6)              ALLOW OUT   Anywhere (v6) on tun0-unrooted
1194 (v6)                  ALLOW OUT   Anywhere (v6) on wlp1s0
# ufw reload  # don't forget to reload firewall after making changes!
Firewall reloaded

(Note that we use insert 1 to make sure the rule is placed in the first position. With UFW, the first rule matched wins.)

Now we can use our dedicated browser to go to the captive portal page and accept the terms and conditions.

Checking traffic with wireshark

You need to be careful, however, not to use any other applications during this time. If you launch Firefox, for example, it can also use the opened ports to communicate with the outside world. I’d like to use Wireshark to see what communications are taking place during this time.

Use wireshark to see what packets are send in the open.
Use wireshark to see what packets are send in the open.

When you are registered with the WiFi provider and are done with the captive portal, you should first disable the profile again with UFW. We can do this by specifying the rule we added earlier, but prepending delete:

# ufw delete allow out to any app Epiphany

Another way to delete rules from UFW is by first doing ufw status numbered and then ufw delete <number>. However, since we have added 6 rules, this may take a while. Also, if you can’t remember the exact rule that was used, you can use ufw show added to show all added rules and their syntax.

Better solutions: beyond UFW

Now, we see that using UFW isn’t exactly ideal to deal with always-on VPN and captive portals. What if you have an email application (or something else) running in the background when you have allowed all those ports to bypass the VPN tunnel? And also, you have to enable and disable the application profile every time you encounter a captive portal. It would be better if we could allow only a single, named, demarcated application to bypass the VPN.

One solution I’ve read about makes use of what I like to call “the Android way”: every installed application is a user with its own home directory. This means that applications don’t have access to each other files, but more importantly, this gives the opportunity to allow only a specific user to access the internet outside of the VPN. This way, we could create a user epiphany that runs Gnome Web to access the captive portal.

AFWall+, an open-source Android application, uses this method to implement a pretty effective firewall. It also uses iptables as a back-end. I might have to finally bite the bullet and learn iptables after all…

Tutorial: rapid GUI development with Qt Designer and PyQt

Confession: I am the opposite of the lazy coder. I like doing things the hard way. Whether it’s developing Java in Vim without code completion, running JUnit tests on the command line (don’t forget to specify all 42 dependencies in the colon-separated classpath!), creating a LaTeX graph that looks “just right”, or writing sqlplus scripts instead of using SQL Developer (GUIs are for amateurs), I always assumed that doing so would make me a better programmer.

So when I was just a fledgling programmer, and I had to design and create some dialog windows for a side project of mine (an awesome add-on to AnkiSRS written in Python and Qt), I did what I always do: find a good resource to learn PyQt and then code everything by hand. Now, since Python is a dynamic language and I used to develop this add-on in Vim, I had no code completion whatsoever, and only the Qt documentation and that tutorial to go by. Making things even more interesting, is that the documentation on Qt is in C++, and is full of stuff like this:

Qt documentation with very readable C++ code for the Python novice.
Qt documentation with very readable C++ code for the Python novice.

Obviously, I had no idea what all the asterisks and ampersands meant and had to use good-old trial and error to see what would stick in Python. Now, on the plus side, I experimented quite a lot with the code, but not having code completion makes it really hard to learn the API. (And even now, using PyCharm, code completion will not always work, because of Python being a dynamically-typed language and all. I am still looking at the Qt documentation quite a bit.)

One thing I noticed, though, is that the guy who develops Anki, Damien Elmes, had all these .ui files lying around, and a bunch more files that read: “WARNING! All changes made to this file will be lost!”. Ugh, generated code! None of the dedicated, soul-cleansing and honest hard work that will shape you as a software developer. It goes without saying I stayed far away from that kind of laziness.

It took me some time to come around on this. One day, I actually got a job as a professional software developer and I had much less time to work on my side-projects. So, when I wanted to implement another feature for my Anki add-on, I found I had too little time to do it the old-fashioned way, and decided to give Qt Designer a go. Surprisingly, I found out it can actually help you tremendously to learn how Qt works (and also save you a bunch of time). Qt Designer allows you to visually create windows using a drag-and-drop interface, then spews out an XML representation of that GUI, which can be converted to code. That generated code will show you possibilities that would have taken a long time and a lot of StackOverflowing to figure out on your own.

So, to help other people discover the wonders of this program, here is a little tutorial on how to do RAD and how to get a dialog window up and running with Qt Designer 4 and Python 3.

Installation

First, we need to install Qt Designer. On Arch Linux, it’s part of the qt4 package and you’ll also need python-pyqt4 for the Python bindings. (On Ubuntu, you have to install both qt4-designer and the python-qt4 packages.)

Our goal

Our goal is to create a simple dialog window that has a text input field where we can type our name, and have a label that will display “Hello there, $userName”. Yes, things will be that exciting. Along the way, we will learn how to assign emitted signals to slots and how to handle events.

Creating a dialog

Fire up Qt Designer, and you will be presented with a “New form” dialog (if you do not see it, go to File > New…).

Qt Designer’s “new form” dialog.
Qt Designer’s “new form” dialog.

For this tutorial, we are going to choose a fairly small “Dialog with Buttons Bottom”:

The main window of Qt Designer.
The main window of Qt Designer.

To the left are the widgets that we can add to our freshly created dialog, to the right, from top to bottom, we see the currently added widgets, the properties of those widgets and the signals and slots currently assigned to the dialog window.

I’m going to add a text label and a so-called line editor to our widget. To make sure they will align nicely, I will put them together in a horizontal container, a QHBoxLayout:

A horizontal box layout with a text label and a line editor.
A horizontal box layout with a text label and a line editor.

In the object inspector, we can see the hierarchy of added widgets:

The object-inspector view
The object-inspector view

This is all simple drag-and-drop: we select a widget from the left pane, drag it to the desired location in the dialog and release the mouse.

Finally, we add two more label: one at the top, instructing the user what to do, and a second one near the bottom, where we soon will display our message.

Two more text labels.
Two more text labels.

Qt Designer provides an easy way to connect signals to slots. If you go to Edit > Edit Signals/Slots (or press F4) you will be presented with a graphical overview of the currently assigned signals and slots. When we start out, the button box at the bottom already emits two signals: rejected and accepted, from the Cancel and Ok button respectively:

The signals and slots of the button box.
The signals and slots of the button box.

The signals rejected() and accepted() are emitted when the cancel and OK buttons are clicked respectively, and the ground symbols indicate the object that is interested in these signals: in this case the dialog window itself. Signals are handled by slots, and so the QDialog will need to have slots for these signals. The slots (or handlers) are named reject() and accept() in this instance, and since they are default slots provided by Qt, they already exist in QDialog, so we won’t have to do anything (except if we want to override their default behavior).

What we want to do now is “catch” the textEdited signal from the line editor widget and create a slot in the dialog window that will handle it. This new slot we’ll call say_hello. So we click the line editor widget and drag a line to anywhere on the dialog window: a ground symbol should be visible:

Assigning a signal to a slot
Assigning a signal to a slot

In the window that appears now, we can select the signal that we are interested in (textEdited) and assign it to a predefined slot, or we can click on Edit… and create our own slot. Let’s do that:

Creating a new slot.
Creating a new slot.

We click the green plus sign, and type in the name of our new slot (say_hello):

Selecting the newly created slot.
Selecting the newly created slot.

The result will now look like:

All signals and slots.
All signals and slots.

In Qt Designer, you can preview your creation by going to Form > Preview… (or pressing Ctrl + R). Notice, however, that typing text in the line editor won’t do anything yet. This is because we haven’t written any implementation code for it. In the next section we will see how to do that.

You can also see the code that will be generated by going to Form > View code…, although this code is going to be in C++.

Okay, enough with designing our dialog window, let’s get to actual Python coding.

Generating code (or not)

When we save our project in Qt Designer, it will create a .ui file, which is just XML containing all the properties (widgets, sizes, signals & slots, etc.) that make up the GUI.

PyQt comes with a program, pyuic4, that can convert these .ui files to Python code. Two interesting command-line options are --preview (or -p for short), that will allow you to preview the dynamically created GUI, and --execute (or -x for short), that will generate Python code that can be executed as a stand-alone. The --output switch (or -o) allows you to specify a filename where the code will be saved.

In our case, creating a stand-alone executable is not going to work, because we have added a custom slot (say_hello) that needs to be implemented first. So we will use pyuic4 to generate the form class:

$ pyuic4 relentless_dialog.ui -o relentless_dialog.py

This is the result:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'relentless_dialog.ui'
#
# Created by: PyQt4 UI code generator 4.12.1
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(
                context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName(_fromUtf8("Dialog"))
        Dialog.resize(250, 320)
        self.buttonBox = QtGui.QDialogButtonBox(Dialog)
        self.buttonBox.setGeometry(QtCore.QRect(10, 270, 221, 41))
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(
                QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
        self.horizontalLayoutWidget = QtGui.QWidget(Dialog)
        self.horizontalLayoutWidget.setGeometry(
                QtCore.QRect(10, 40, 231, 80))
        self.horizontalLayoutWidget.setObjectName(
                _fromUtf8("horizontalLayoutWidget"))
        self.horizontalLayout = QtGui.QHBoxLayout(
                self.horizontalLayoutWidget)
        self.horizontalLayout.setMargin(0)
        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.label = QtGui.QLabel(self.horizontalLayoutWidget)
        self.label.setObjectName(_fromUtf8("label"))
        self.horizontalLayout.addWidget(self.label)
        self.lineEdit = QtGui.QLineEdit(self.horizontalLayoutWidget)
        self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
        self.horizontalLayout.addWidget(self.lineEdit)
        self.label_2 = QtGui.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(0, 10, 251, 20))
        self.label_2.setObjectName(_fromUtf8("label_2"))
        self.label_3 = QtGui.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(2, 190, 241, 20))
        self.label_3.setObjectName(_fromUtf8("label_3"))

        self.retranslateUi(Dialog)
        QtCore.QObject.connect(self.buttonBox,
                               QtCore.SIGNAL(_fromUtf8("accepted()")),
                               Dialog.accept)
        QtCore.QObject.connect(self.buttonBox,
                               QtCore.SIGNAL(_fromUtf8("rejected()")),
                               Dialog.reject)
        QtCore.QObject.connect(
                self.lineEdit,
                QtCore.SIGNAL(_fromUtf8("textChanged(QString)")),
                Dialog.say_hello)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(_translate("Dialog", "Dialog", None))
        self.label.setText(_translate("Dialog", "Name:", None))
        self.label_2.setText(_translate(
                             "Dialog",
                             "<html><head/><body><p align="center">" +
                             "Please enter your name</p></body></html>",
                             None))
        self.label_3.setText(_translate(
                             "Dialog",
                             "<html><head/><body><p align="center">" +
                             "[Where our message will appear]" +
                             "</p></body></html>",
                             None))

It is here that you can learn a lot about how PyQt works, even though some statements seem a bit baroque, like the binding of the textChanged signal to our custom slot:

QtCore.QObject.connect(
        self.lineEdit,
        QtCore.SIGNAL(_fromUtf8("textChanged(QString)")),
        Dialog.say_hello)

If you would write this yourself, you would probably prefer:

self.lineEdit.textChanged[str].connect(Dialog.say_hello)

(Incidentally, the text between the square brackets in textChanged[str] indicates that a single argument of this type is passed to the slot.)

Bringing it all together

Having generated our form class, we can now create a base class that will use this class:

import sys
from PyQt4 import QtGui
from relentless_dialog import Ui_Dialog

class MyDialog(QtGui.QDialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.show()

    def say_hello(self, user_text):
        text = "Hello there, {0}!".format(user_text)
        self.ui.label_3.setText(text)

def main():
    app = QtGui.QApplication(sys.argv)
    dialog = MyDialog()
    app.exec_()

if __name__ == "__main__":
    main()

When we are passing self to self.ui.setupUi, we are passing the widget (a QDialog) in which the user interface will be created.

We implement our custom slot by defining a method say_hello that takes a single argument (the user-provided text from the line editor). We craft a witty sentence with it, and set it as the label text.

As mentioned before, we could also dynamically create the GUI directly from the .ui file:

import sys
from PyQt4 import QtGui
from PyQt4 import uic

class MyDialog(QtGui.QDialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        dialog = uic.loadUi("relentless_dialog.ui", self)
        dialog.show()

# etc

Running the application

Running this will show the following (drumroll):

Running the application.
Running the application.