What's New in Qt 5.3: QPrinterInfo

Jeff Tranter


Jeff Tranter is a Qt Consulting Manager at ICS. Jeff oversees the architectural and high-level design of software systems for clients. Jeff’s leadership organizes global teams of developers on desktop and embedded applications.

By Jeff Tranter | Wednesday, July 30, 2014

The Qt 5.3 release made a lot of "under the hood" improvements to the internals of the Qt printing system. There were also some changes visible at the API level. One of the more noticeable changes was to add enhancements to the QPrinterInfo class. In this blog post, I'll go over this class and present an example application illustrating how to use it.

The QPrinterInfo (1) class returns information about printers available on a machine and has been included in Qt since version 4.4. It is part of Qt's PrintSupport module, so when using qmake as your build system, you need to enable it in your project file with a line like the following:

    QT += printsupport

Qt 5.3 now uses the CUPS Printing System (2) (CUPS is the standards-based, open source printing system developed by Apple Inc. for OS X® and other UNIX®-like operating systems) as the printing back end on Linux systems. Different back ends are used on Windows and MacOS. Qt 5.3.0 introduced some new methods to the QPrinterInfo class. This is a reasonably small class and is easy to understand by reading the Qt API documentation.

I whipped up a small example application (3) that demonstrates the class and exercises most of the methods. It lists the printers on the system in a combo box. For the selected printer, it lists the information that QPrinterInfo returns about it. A couple of screen shots of the program running on a Linux system are shown below:

Let's step through the code to illustrate how the class works. Our main program, main.cpp, is the standard boilerplate code for most widget applications and was generated by the Qt Creator IDE. It just creates a QApplication and an instance of the new class we will create, called Widget. This should be familiar to any Qt developer but I show it here for completeness.

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

We will create our UX in a new class derived from QWidget called Widget. In a real application, with a more complete user interface, we would likely have derived it from QMainWindow. Again, this is all standard boilerplate code that Qt Creator generated for me. The only addition to the code was to add a slot called UpdateUI, which will be described later. Here is the code for widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

public slots:
    void UpdateUI();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

Finally, we get to the code that implements the Widget class and uses PrinterInfo. The code's include files and constructor are standard. There is a possibility that the system does not have any printers configured. To handle that case, I simply call the static method availablePrinterNames and display a message box if the list of printers is empty. This method is new in Qt 5.3, and it is recommended to be used instead of the similar one availablePrinters, as it will be faster on most systems because it only needs to return a list of the printer names and not the QPrinterInfo objects for each printer.

We also call the method again to populate the combo box of printer names.

The display of information for a specific printer will be done in the method (a slot) called UpdateUI. We connect the combo box's currentIndexChanged method to this slot so that it is called whenever the user changes the selected printer. Finally, we call UpdateUI once to update the information for the initially selected printer.

#include <QMessageBox>
#include <QPrinterInfo>
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    if (QPrinterInfo::availablePrinterNames().isEmpty()) {
        QMessageBox::critical(this, tr("Error"), tr("No printers found. The application will not display any useful information."));
    }

    ui->setupUi(this);
    ui->printerComboBox->addItems(QPrinterInfo::availablePrinterNames());

    // Update UI when selected printer is changed.
    connect(ui->printerComboBox,SIGNAL(currentIndexChanged(int)), SLOT(UpdateUI()));

    // Initially display information for first printer.
    UpdateUI();
}

The destructor for the class is trivial, but for completeness, here it is:

Widget::~Widget()
{
    delete ui;
}

Now for the implementation of the slot, UpdateUI, that updates the widget with information for the selected printer. I've added comments (in green) to the code below to explain how it works.

void Widget::UpdateUI() {

    // We get the currently selected printer's name from the combo box, then get the QPrinterInfo
    //object for that printer by calling the static method QPrinterInfo::printerInfo.
    QString name = ui->printerComboBox->currentText();
    QPrinterInfo info = QPrinterInfo::printerInfo(name);

    // It is good practice to make sure the object is valid, so we call isNull to do a sanity
    // check in case there is no printer info.
    if (info.isNull()) {
            return;
    }

    // We call various QPrinterInfo methods like description() and fill in the user interface.
    // Some of these methods are new in Qt 5.3
    ui->descriptionLabel->setText(info.description());
    ui->defaultPrinterLabel->setText(info.isDefault() ? tr("Yes") : tr("No"));
    ui->isNullLabel->setText(info.isNull() ? tr("Yes") : tr("No"));
    ui->isRemoteLabel->setText(info.isRemote() ? tr("Yes") : tr("No"));
    ui->locationLabel->setText(info.location());
    ui->makeAndModelLabel->setText(info.makeAndModel());

    // The method to return the printer state is new in Qt 5.3. We display it using suitable (localized) text.
    // Since this can changed dynamically, ideally it should be updated with a timer so that it reflects the current status when it
    // changes. This is simple to do, but I omitted it as it would obscure the clarity of the example code.
    switch (info.state()) {
        case QPrinter::Idle:
            ui->stateLabel->setText(tr("Idle"));
            break;
        case QPrinter::Active:
            ui->stateLabel->setText(tr("Active"));
            break;
        case QPrinter::Aborted:
            ui->stateLabel->setText(tr("Aborted"));
            break;
        case QPrinter::Error:
            ui->stateLabel->setText(tr("Error"));
            break;
        default:
            ui->stateLabel->setText(tr("Unknown"));
            break;
    }

    // All of the methods below are new in Qt 5.3.
    ui->defaultPageSizeLabel->setText(info.defaultPageSize().name());
    ui->minimumPageSizeLabel->setText(info.minimumPhysicalPageSize().name());
    ui->maximumPageSizeLabel->setText(info.maximumPhysicalPageSize().name());
    ui->customPageSizesLabel->setText(info.supportsCustomPageSizes() ? tr("Yes") : tr("No"));

    // We fill in the combox box with a description of the screen resolutions.
    ui->resolutionsComboBox->clear();
    foreach (int res, info.supportedResolutions()) {
        ui->resolutionsComboBox->addItem(QString::number(res) + tr(" dpi"));
    }

    // We do the same for the page sizes.
    ui->pageSizesComboBox->clear();
    foreach (QPageSize size, info.supportedPageSizes()) {
        ui->pageSizesComboBox-> addItem(size.name());
    }
}

I noticed a few quirks on my Kubuntu 14.04 (the KDE desktop version of Ubuntu) Linux system. An empty string is always returned for the printer location. This seems to be an issue with the KDE printer configuration panel not properly setting it in CUPS, and not the fault of Qt. The printer description also seems to be based on the name of the printer driver used. Again, this seems to be due to the KDE printer control panel, which generated it and did not provide a way to change it.

The example application should also run on Windows or Mac OS with no changes. Note that the Qt PrintSupport module is not supported on some platforms. Android is one example, where printing support was only recently added to the operating system and is not yet implemented in Qt. On unsupported platforms such as Android, you can call the PrintSystem APIs, but they do not return any useful information.

Summary

If you need to perform printer-related functions, the QPrinterInfo class should prove very handy. The added methods in Qt 5.3 fill some holes that were there previously and make it more useful. It is good to see that Qt's print support continues to improve and that Qt on the desktop is still actively being developed despite all the attention given to embedded devices.

References

  1. QPrinterInfo class documentation, accessed July 23, 2014, qt-project.org/doc/qt-5/qprinterinfo.html
  2. CUPS Printing System web site, accessed July 23, 2014, www.cups.org
  3. Source code for example program, printerinfo.zip


Have a question or add to the conversation: Log in Register