While most development tasks can be solved with buttons, text editing widgets, and other components provided by PyQt, you may at some point find yourself in a situation where no single widget provides you with the tools or functionality that you need. You might even find yourself needing to use a widget you made in other GUIs and therefore need a way to easily import your custom-made widget into other applications. Thankfully, PyQt allows developers to build and import their own widgets for solving new and unforeseen tasks.
Find out about creating your own custom widgets in PyQt
See how to apply the custom widget built in a small example GUI
Learn about Qt’s four image handling classes
Use a new widget, QSlider, for selecting values in a bounded range
Let’s learn about the custom widget we’ll build in the following sections.
Project 12.1 – RGB Slider Custom Widget
For this chapter’s project, we are going to take a look at making a custom, functional widget in PyQt. While PyQt offers a variety of widgets for building GUIs, every once in a while you may find yourself needing to design and build your own. One of the benefits of creating a customized widget is that you can either create a general widget that can be used by many different applications or make an application-specific widget that allows you to solve a specific problem.
Modifying the properties of PyQt’s widgets by using built-in methods, such as setAlignment(), setTextColor(), and setRange()
Creating style sheets to change a widget’s existing behavior and appearances
Subclassing widgets and reimplementing event handlers, or adding properties dynamically to QWidget classes
Creating composite widgets that are made up of two more types of widgets and arranged together using a layout
Designing a completely new widget that subclasses QWidget and has its own unique properties and appearance
Before finding out how to make the RGB slider widget, we’ll need to learn a little more about some of the classes we will need to build the application.
PyQt’s Image Handling Classes
In previous examples, we worked with QPixmap to handle image data. Qt actually provides four different classes for working with images, each with their own special purpose.
QPixmap is the go-to choice for displaying images on the screen. Pixmaps can be presented on a variety of widgets that can display icons, including QLabel and QPushButton. QImage is optimized for reading, writing, and manipulating images and is very useful if you need to directly access and modify an image’s pixel data. QImage can also act as a paint device. A paint device (created by the QPaintDevice class) is a two-dimensional surface that can be drawn on using QPainter. It is also worth noting that QImage inherits QPaintDevice.
Conversion between QImage and QPixmap is also possible. One possibility for using the two classes together is to load an image file with QImage, manipulate the image data, and then convert the image to a pixmap before displaying it on the screen. The RGB slider widget gives an example for converting between the two classes.
QBitmap is a subclass of QPixmap and provides monochrome (1-bit depth) pixmaps. QPicture is a paint device that replays QPainter commands, meaning you can create a picture from whatever image format you are painting on. Pictures created with QPicture are resolution independent, appearing the same no matter what image format you use, such as png, svg, or pdf.
The RGB slider uses two types of widgets for selecting RGB values: QSpinBox, which was introduced in Chapter 4, and a new widget.
The QSlider Widget
The QSlider class provides a developer with a tool for selecting integer values within a bounded range. Sliders provide users with a convenient means for quickly selecting values or changing settings with only the slide of a simple handle. By default, sliders are arranged vertically (specified by Qt.Orientation.Vertical), but that can be changed by passing the flag Qt.Orientation.Horizontal to the constructor.
Here, the slider’s maximum range is 200, and its value is printed to the shell whenever the slider’s position changes.
Explanation for the RGB Slider Widget
The RGB slider is a custom widget created by combining a few of Qt’s built-in widgets: QLabel, QSlider, and QSpinBox. The appearance of the sliders is adjusted using style sheets so that they give visual feedback to the user about which RGB value they are adjusting. The sliders and spin boxes are connected together so that their values are in sync and so that the user can see the integer value on the RGB scale. The RGB values are also converted to hexadecimal format and displayed on the widget.
The sliders and spin boxes can be used to either find out the RGB or hexadecimal values for a color or use the reimplemented mousePressEvent() method so that a user can click on a pixel in an image to find out the pixel’s RGB value. An example of this is shown in the “RGB Slider Demo” section, where you will also see how to import the RGB slider in a demo application.
The imports for the RGB slider
The style sheet for the RGB slider, part 1
The sliders use linear gradients so that users can get a visual representation of how much of the red, green, and blue colors are being used. With linear gradients, the color is interpolated from x1, y1 to x2, y2. The pseudostate horizontal is used to specify that the styles will be applied to horizontal QSlider objects.
The groove subcontrol refers to the long, rectangular part of the slider, which is solid white before moving the handle of the slider. The add-page subcontrol denotes the color of the slider parts before the handle, and sub-page denotes the color after. For the handle, the color will change whenever the mouse hovers over it.
The style sheet for the RGB slider, part 2
Code to start building the RGBSlider class
The current_val instance variable will be used to keep track of the current RGB color value. The color, of course, will be composed by the slider and spin box values.
Code for the setUpMainWindow() method in the RGBSlider class, part 1
The contents of cd_label are then scaled to fit the window’s size.
Updating the Sliders and Spin Boxes
Code for the setUpMainWindow() method in the RGBSlider class, part 2
Code for the setUpMainWindow() method in the RGBSlider class, part 3
From there, the sliders, spin boxes, and container for the labels are organized in a QGridLayout.
Updating the Colors
QSlider and QSpinBox can both emit the valueChanged signal. We can connect the sliders and spin boxes so that their values change relative to each other. For example, when red_slider emits a signal, it triggers the updateRedSpinBox() slot, which then updates the red_spinbox value using setValue(). A similar process happens for the red_spinbox. This process also happens for the sliders and spin boxes that control the blue and green values.
Code for the setUpMainWindow() method in the RGBSlider class, part 4
All of the widgets along with cd_label from Listing 12-5 are contained in rgb_widgets and arranged in the main layout.
Code for the slots that update the slider and spin box values
When a valueChanged signal triggers a slot, it uses value to update the corresponding slider or spin box and then calls a function that will create a new color from the red, green, or blue values.
Code for methods that create and update a color
Continuing with red, the redValue() function creates a new qRgb color, using the new red value and the current_val’s green() and blue() colors. The variable current_val is an instance of QColor. The QColor class has functions that we can use to access an image’s RGB (or other color format) values.
The new_color is then passed to updateColorInfo(). Green and blue colors are handled in a similar fashion. Next, we have to create a QColor from the qRgb value and store it in current_val. The QImage color_display is updated with fill(), which is then converted to a QPixmap, and displayed on the cd_label.
The last thing to do is to update the hexadecimal labels using QColor.name(). (Remember that current_val is a QColor object.) This function returns the name of the color in the format “#RRGGBB”.
Adding Methods to a Custom Widget
The options for methods that you could create for a custom widget are numerous. One option is to create methods that allow the user to modify the behavior or appearance of your custom widget. Another option is to use the event handlers to check for keyboard or mouse events that could be used to interact with your GUI.
Code for the getPixelValues() method
Go ahead and run the script now and see how it operates. Right now, the application is simply its own small GUI. Let’s see how to use the custom widget class in another application to utilize the color selecting feature.
RGB Slider Demo
One reason for creating a custom widget is so that it can be used in other applications. The following program is a short example of how to import and set up the RGB slider built in Project 12.1. For this example, an image is displayed in the window alongside the RGB slider. Users can click on points within the image and see the RGB and hexadecimal values change in real time.
Explanation for the RGB Slider Demo
Be sure to download the image from the images folder in the GitHub repository.
Code that shows an example for using the RGB slider widget
In the MainWindow class, set up the window in initializeUI(), load an image, and create an instance of the RGB slider. The widgets are then arranged in the window.
For this application, we are still creating the image as an instance of QImage and then converting it to a QPixmap. QImage is used so that we have access to the image’s pixel information.
To use the getPixelValues() method in the RGBSlider class, we’ll need to reimplement the QLabel object’s mouse event handler. When the user clicks on a pixel in the image, the x and y coordinates from the event are used to update the values in the RGB slider widget using the getPixelValues() method.
If you only want to use the slider to get different RGB or hexadecimal values, then the application is finished. But you could continue to add other functionalities to the RGB slider to use in your own projects.
Summary
Not every problem can be solved by the widgets that Qt provides. In situations where ingenuity is needed, PyQt is great because it allows developers to design, build, and customize their own widgets. This can be handled in a variety of ways, perhaps by building a new widget from preexisting widgets or by creating completely new widgets from scratch. From there, a new widget can be seamlessly imported into other applications.
In Chapter 13, you will find out how to create modern-looking GUIs using Qt Quick.