© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
J. M. WillmanBeginning PyQthttps://doi.org/10.1007/978-1-4842-7999-1_10

10. Presenting Data in PyQt

Joshua M Willman1  
(1)
Sunnyvale, CA, USA
 

As a GUI developer, you’ll probably find yourself at some point looking for a way to present data, whether textual or visual, in your interface. Before embarking on that adventure, you’ll need to keep in mind the user and the purpose of your application. In many cases, the methods for presenting graphical data already follow various standard practices. Many users will have a preexisting notion of how data in tables should be displayed or how items in a list should be added to deleted. Thankfully, Qt has already considered this and created a few classes that make the task of presenting data quicker and simpler.

In this chapter, you will
  • Begin thinking about how to handle data in PyQt

  • Create a GUI for each of the item-based convenience classes – QListWidget, QTableWidget, and QTreeWidget

  • Add drag-and-drop functionality to a GUI

  • Create context menus for displaying shortcuts

Let’s start by finding out what the convenience classes can do.

Quickly Handling Data in PyQt

The study and collection of data is a great undertaking, especially since information has the potential of improving people’s lives, informing decisions, finding solutions, and more. The process of organizing and visualizing data is simplified even more thanks to Qt’s Model/View design pattern. This topic is covered in more detail in Chapter 14, but what is important to understand here is that the model and view work together to organize, manage, and present data in a GUI. Models are used for managing the data, while views are used for displaying the data in the GUI.

For this chapter, we’ll focus on the convenience classes that are derived from the Model/View classes. While this means you’ll get less customizability and flexibility, the convenience classes still provide the general functionalities you will need right out of the box. This can be especially helpful when customization is not required for your application.

The three convenience classes use predefined models and views with all of the standard styles, features, and functionalities that you would expect to find in a general item-based interface.

Information about Model/View programming can be found at https://doc.qt.io/qt-6/model-view-programming.html.

The QListWidget Class

The QListWidget class creates a widget that displays a single column of items, making it simpler for adding and removing items. Items can be added either when the widget is created in code or inserted later through the GUI.

Items in all three of the convenience classes are created using special classes. The QListWidgetItem class is used in conjunction with QListWidget to serve as an item that can be used with the list. Figure 10-1 shows the GUI we are going to make for this example.
Figure 10-1

QListWidget can be used to display objects in an inventory or items in a directory

The QListWidget class includes various methods for creating and manipulating data in the list, including
  • addItem(QListWidgetItem) – Adds an item to the end of a list

  • currentRow() – Returns the index value of the currently selected row

  • insertItem(row, QListWidgetItem) – Inserts an item at the specified row

  • takeItem(row) – Removes an item from the specified row

  • clear() – Removes and deletes all items from the list

Explanation for Using QListWidget

The following example briefly demonstrates how to add, insert, remove, and clear all items from a QListWidget. To begin, use the basic_window.py script from Chapter 1 to create a new script and set up the MainWindow class and initializeUI() method in Listing 10-1.
# list_widget.py
# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget,
    QPushButton, QListWidget, QListWidgetItem, QInputDialog,
    QHBoxLayout, QVBoxLayout,)
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(400, 200)
        self.setWindowTitle("QListWidget Example")
        self.setUpMainWindow()
        self.show()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())
Listing 10-1

Setting up the MainWindow class for the QListWidget example

Be sure to include the QListWidget and QListWidgetItem imports from QtWidgets. QInputDialog will be used when adding or inserting a new row into the QListWidget.

The setUpMainWindow() method for the MainWindow class is built in Listing 10-2. QListWidget is used to manage the data items displayed in the GUI window. For alternating row colors, set the setAlternatingRowColors() method’s value to True.
# list_widget.py
    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        self.list_widget = QListWidget()
        self.list_widget.setAlternatingRowColors(True)
        # Initialize the QListWidget with items
        grocery_list = ["grapes", "broccoli", "garlic",
                        "cheese", "bacon", "eggs", "waffles",
                        "rice", "soda"]
        for item in grocery_list:
            list_item = QListWidgetItem()
            list_item.setText(item)
            self.list_widget.addItem(list_item)
        # Create buttons for interacting with the items
        add_button = QPushButton("Add")
        add_button.clicked.connect(self.addListItem)
        insert_button = QPushButton("Insert")
        insert_button.clicked.connect(self.insertItemInList)
        remove_button = QPushButton("Remove")
        remove_button.clicked.connect(self.removeOneItem)
        clear_button = QPushButton("Clear")
        clear_button.clicked.connect(self.list_widget.clear)
        # Create layouts
        right_v_box = QVBoxLayout()
        right_v_box.addWidget(add_button)
        right_v_box.addWidget(insert_button)
        right_v_box.addWidget(remove_button)
        right_v_box.addWidget(clear_button)
        main_h_box = QHBoxLayout()
        main_h_box.addWidget(self.list_widget)
        main_h_box.addLayout(right_v_box)
        self.setLayout(main_h_box)
Listing 10-2

Creating the setUpMainWindow() method for the QListWidget example

To display the string items from grocery_list in list_widget, we’ll need to create a QListWidgetItem object for each item, set its text using setText(), and use the addItem() method to add the item to list_widget. These items will populate the list when the program begins. Here, we are merely passing text to QListWidgetItem, but you can also pass an icon as well.

From there, let’s create the buttons for each of the actions that can be performed on the QListWidget and connect those buttons to a clicked signal. Whenever a button is clicked, it will emit a signal that connects to a slot for editing the data items in the QListWidget.

The buttons are added to a QVBoxLayout, which are then arranged along with list_widget in the main window’s QHBoxLayout.

Listing 10-3 takes care of creating the slot that is triggered by add_button.
# list_widget.py
    def addListItem(self):
        """Add a single item to the list widget."""
        text, ok = QInputDialog.getText(
            self, "New Item", "Add item:")
        if ok and text != "":
            list_item = QListWidgetItem()
            list_item.setText(text)
            self.list_widget.addItem(list_item)
Listing 10-3

Code for the addListItem() slot

When the user wants to add a new item, a QInputDialog instance like the one seen in Figure 10-2 will appear.
Figure 10-2

Adding a new item to QListWidget using QInputDialog

If text has been entered and the user clicks the OK button in the dialog, then a new item is appended to the end of list_widget using addItem().

Using the buttons on the right of the main window, the user can also insert, remove, or clear the list items. We’ll need to create two additional slots, insertItemInList() and removeOneItem(), in Listing 10-4. For clear_button, the clicked signal is simply connected to the QListWidget method clear().
# list_widget.py
    def insertItemInList(self):
        """Insert a single item into the list widget under
        the currently selected row. """
        text, ok = QInputDialog.getText(
            self, "Insert Item", "Insert item:")
        if ok and text != "":
            row = self.list_widget.currentRow()
            row = row + 1 # Select row below current row
            new_item = QListWidgetItem()
            new_item.setText(text)
            self.list_widget.insertItem(row, new_item)
    def removeOneItem(self):
        """Remove a single item from the list widget."""
        row = self.list_widget.currentRow()
        item = self.list_widget.takeItem(row)
        del item
Listing 10-4

The remaining slots for the MainWindow class in the QListWidget example

A QInputDialog will appear when the user wants to insert a new data item. If the user clicks OK, the currently selected row is determined using currentRow(). Next, the value for row is increased by 1, a new QListWidgetItem is created, and that new item is inserted below the currently selected row. When removing a row, currentRow() is used again to discover which row is selected. The method takeItem() is used to remove the item from the QListWidget. The del keyword is used to permanently delete an item since takeItem() does not actually delete items.

In the following section, you’ll continue to use QListWidget to find out how to extend the capabilities of the widget with drag-and-drop functionality.

Drag and Drop in PyQt

The drag-and-drop mechanism allows a user to perform tasks in a GUI by selecting items, such as icons or images, and moving them into another window or onto another object. PyQt also makes including this behavior in an application very straightforward. To allow widgets to have basic drag-and-drop functionality, you only need to set the values of setAcceptDrops() and setDragEnabled() methods to True.

With drag-and-drop functionality enabled, you can move items from one text edit, list, or table object to another in PyQt. QMimeData can also be used to handle what kind of data can be moved, dragged, or dropped.

Figure 10-3 displays the GUI you’ll be creating in this section. Items in the window can be dragged and dropped back and forth between the two QListWidget instances. Be sure to download the images folder from the GitHub repository for this project.
Figure 10-3

Two QListWidget objects used to demonstrate drag and drop

Tip

Drag-and-drop mechanics can be applied to a variety of different widgets, not just QListWidget. You should have a look at PyQt or Qt documentation to find out which widgets already have built-in drag-and-drop capabilities.

Explanation for Drag and Drop

Start with the basic_window.py script and modify the MainWindow class in Listing 10-5. We’ll continue using the QListWidget and QListWidgetItem classes in this example to demonstrate drag and drop.
# drag_drop.py
# Import necessary modules
import sys, os
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel,
    QListWidget, QListWidgetItem, QGridLayout)
from PyQt6.QtCore import QSize
from PyQt6.QtGui import QIcon
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(500, 300)
        self.setWindowTitle("Drag and Drop Example")
        self.setUpMainWindow()
        self.show()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())
Listing 10-5

Creating the MainWindow class for the drag and drop example

There are two instances of the QListWidget class created, icon_widget and list_widget, in Listing 10-6. Items in QListWidget can be viewed either as icons or as text in a list. The icon_widget object displays icons by using the flag IconMode. The default setting is to show items in a list.
# drag_drop.py
    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        icon_label = QLabel("ICONS", self)
        icon_widget = QListWidget()
        icon_widget.setAcceptDrops(True)
        icon_widget.setDragEnabled(True)
        icon_widget.setViewMode(
            QListWidget.ViewMode.IconMode)
        image_path = "images"
        for img in os.listdir(image_path):
            list_item = QListWidgetItem()
            list_item.setText(img.split(".")[0])
            list_item.setIcon(QIcon(os.path.join(image_path,
                                "{0}").format(img)))
            icon_widget.setIconSize(QSize(50, 50))
            icon_widget.addItem(list_item)
        list_label = QLabel("LIST", self)
        list_widget = QListWidget()
        list_widget.setAlternatingRowColors(True)
        list_widget.setAcceptDrops(True)
        list_widget.setDragEnabled(True)
        # create grid layout
        grid = QGridLayout()
        grid.addWidget(icon_label, 0, 0)
        grid.addWidget(list_label, 0, 1)
        grid.addWidget(icon_widget, 1, 0)
        grid.addWidget(list_widget, 1, 1)
        self.setLayout(grid)
Listing 10-6

Code for the setUpMainWindow() method in the drag and drop example

To set up the drag-and-drop ability for icon_widget, set the values for setAcceptDrops() and setDragEnabled() to True. The setAcceptDrops() method allows for drop events to be accepted on the widget, while setDragEnabled() allows for items to be dragged in and out of the widget. These methods are actually inherited from QWidget, so most classes that inherit QWidget will also have access to drag-and-drop functionalities.

When the program begins, only the QListWidget icon_widget will be populated with items from the images folder. Although the methods setText() and setIcon() are called to apply the text and icons to the QListWidgetItem instances, these values can also be passed as arguments when instantiating a QListWidgetItem object.

Next, repeat the process for list_widget, but don’t change the widget’s view mode. When one of the icons that are loaded into icon_widget is dragged onto list_widget, the list updates its contents to include the new item. Dropping an item from one QListWidget to the other adds a new item to that list.

The next section will take a look at the convenience class for creating tables in PyQt.

The QTableWidget Class

The QTableWidget class provides a means to display and organize data in tabular form, presenting the information in rows and columns. Using tables arranges data into a more readable format. An example of PyQt’s tables can be seen in Figure 10-4.
Figure 10-4

Example of a table from the QTableWidget class

QTableWidget provides you with the standard tools that you will need to create tables, including the ability to edit cells, set the number of rows and columns, and add vertical or horizontal header labels. You can also hide headers should you not want them to be visible. QTableWidget also has a number of signals for checking if cells or items have been clicked, double-clicked, or even altered.

For this first example, we will be taking a look at how to use QTableWidget to create the foundation for an application to edit spreadsheets. In addition, this application will teach you how to build a context menu to manipulate the contents of the table widget.

Explanation for Using QTableWidget

For this application, start by building the MainWindow class in Listing 10-7 by using the main_window_template.py script from Chapter 5. Make sure to import QTableWidget and QTableWidgetItem, which is used to create items for the table widget. The QMenu class will be used to create context menus in the GUI.
# table_widget.py
# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow,
    QTableWidget, QTableWidgetItem, QMenu, QInputDialog)
from PyQt6.QtGui import QAction
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(1000, 500)
        self.setWindowTitle(
            "Spreadsheet - QTableWidget Example")
        # Used for copy and paste actions
        self.item_text = None
        self.setUpMainWindow()
        self.createActions()
        self.createMenu()
        self.show()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())
Listing 10-7

Setting up the MainWindow class for the QTableWidget example

Be sure to create the instance variable, item_text, that will hold the text for copy and paste actions. The QTableWidget that creates the GUI’s spreadsheet is created in Listing 10-8 in setUpMainWindow().
# table_widget.py
    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        self.table_widget = QTableWidget()
        # Set initial row and column values
        self.table_widget.setRowCount(10)
        self.table_widget.setColumnCount(10)
        # Set focus on cell in the table
        self.table_widget.setCurrentCell(0, 0)
        # When the horizontal headers are double-clicked,
        # emit a signal
        h_header = self.table_widget.horizontalHeader()
        h_header.sectionDoubleClicked.connect(
            self.changeHeader)
        self.setCentralWidget(self.table_widget)
Listing 10-8

Creating the setUpMainWindow() method for the QTableWidget example

When instantiating a QTableWidget object, you could pass the number of rows and columns as parameters to the QTableWidget, like in the following line:
        table_widget = QTableWidget(10, 10)

Or you could construct a table using the setRowCount() and setColumnCount() methods. The table_widget instance will start with ten rows and ten columns.

The setCurrentCell() method can be used to place focus on a specific cell in the table.

The QTableWidget and QTreeWidget classes have headers; QListWidget does not. To access a table’s headers, you can either call the horizontalHeader() for horizontal headers or verticalHeader() for vertical ones. Changing header labels in QTableWidget can either be done directly in code or by using a slightly indirect approach. Headers for tables are created using QHeaderView in the QTableView class . We’ll cover QTableView and other view classes more in Chapter 14.

Since QTableWidget inherits from the QTableView class, we also have access to its functions. Knowing that, we are able to obtain the QHeaderView object using table_widget.horizontalHeader(). From there, we can connect to the QHeaderView class's signal, sectionDoubleClicked. This signal can be used to check if the user double-clicked a header section. If they did, a signal triggers the changeHeader() slot (created in Listing 10-12).

The menu bar seen in Figure 10-4 contains two menus: File and Table. File contains the action for quitting the application. Table includes actions for adding and deleting rows or columns. We’ll set up those actions in Listing 10-9.
# table_widget.py
    def createActions(self):
        """Create the application's menu actions."""
        # Create actions for File menu
        self.quit_act = QAction("Quit", self)
        self.quit_act.setShortcut("Ctrl+Q")
        self.quit_act.triggered.connect(self.close)
        # Create actions for Table menu
        self.add_row_above_act = QAction(
            "Add Row Above", self)
        self.add_row_above_act.triggered.connect(
            self.addRowAbove)
        self.add_row_below_act = QAction(
            "Add Row Below", self)
        self.add_row_below_act.triggered.connect(
            self.addRowBelow)
        self.add_col_before_act = QAction(
            "Add Column Before", self)
        self.add_col_before_act.triggered.connect(
            self.addColumnBefore)
        self.add_col_after_act = QAction(
            "Add Column After", self)
        self.add_col_after_act.triggered.connect(
            self.addColumnAfter)
        self.delete_row_act = QAction("Delete Row", self)
        self.delete_row_act.triggered.connect(self.deleteRow)
        self.delete_col_act = QAction("Delete Column", self)
        self.delete_col_act.triggered.connect(
            self.deleteColumn)
        self.clear_table_act = QAction("Clear All", self)
        self.clear_table_act.triggered.connect(
            self.clearTable)
Listing 10-9

Code for the createActions() method in the QTableWidget example

The slots that each action is connected to are created in Listing 10-14. The menu items are created in Listing 10-10. Refer to Chapter 5 for more reference on creating actions and menus.
# table_widget.py
    def createMenu(self):
        """Create the application's menu bar."""
        self.menuBar().setNativeMenuBar(False)
        # Create file menu and add actions
        file_menu = self.menuBar().addMenu('File')
        file_menu.addAction(self.quit_act)
        # Create table menu and add actions
        table_menu = self.menuBar().addMenu('Table')
        table_menu.addAction(self.add_row_above_act)
        table_menu.addAction(self.add_row_below_act)
        table_menu.addSeparator()
        table_menu.addAction(self.add_col_before_act)
        table_menu.addAction(self.add_col_after_act)
        table_menu.addSeparator()
        table_menu.addAction(self.delete_row_act)
        table_menu.addAction(self.delete_col_act)
        table_menu.addSeparator()
        table_menu.addAction(self.clear_table_act)
Listing 10-10

Code for the createMenu() method in the QTableWidget example

The context menu and its actions are generated in the next section.

Creating Context Menus

This application also introduces how to create a context menu, sometimes called a pop-up menu, that appears in the window due to a user’s interaction, such as when the right mouse button is clicked. A context menu displays a list of general commands, such as Back Page or Reload Page. Context menus can also be set for managing specific widgets.

Since context menus are caused by events, we can reimplement the event handler, contextMenuEvent(). A simple example is shown in the following block of code:
    def contextMenuEvent(self, event):
        context_menu = QMenu(self)
        context_menu.addAction(self.add_row_above_act)
A context menu is typically created using QMenu. You can either use existing actions that are created in the menu bar or the toolbar, or you can create new ones. An example of this application’s context menu is shown in Figure 10-5.
Figure 10-5

Example of a context menu that displays actions for editing the table widget

For the context menu in Figure 10-5, all of the menu’s actions in Listing 10-9 (except for quit_act) are included. Two additional actions are also created specifically for the context menu: copy_act and paste_act. Those are handled in Listing 10-11.
# table_widget.py
    def contextMenuEvent(self, event):
        """Create context menu and additional actions."""
        context_menu = QMenu(self)
        context_menu.addAction(self.add_row_above_act)
        context_menu.addAction(self.add_row_below_act)
        context_menu.addSeparator()
        context_menu.addAction(self.add_col_before_act)
        context_menu.addAction(self.add_col_after_act)
        context_menu.addSeparator()
        context_menu.addAction(self.delete_row_act)
        context_menu.addAction(self.delete_col_act)
        context_menu.addSeparator()
        # Create actions specific to the context menu
        copy_act = context_menu.addAction("Copy")
        paste_act = context_menu.addAction("Paste")
        context_menu.addSeparator()
        context_menu.addAction(self.clear_table_act)
        # Execute the context_menu and return the action
        # selected. mapToGlobal() translates the position
        # of the window coordinates to the global screen
        # coordinates. This way we can detect if a right-click
        # occurred inside of the GUI and display the context
        # menu
        action = context_menu.exec(
            self.mapToGlobal(event.pos()))
        # Check for actions selected in the context menu that
        # were not created in the menu bar
        if action == copy_act:
            self.copyItem()
        if action == paste_act:
            self.pasteItem()
Listing 10-11

Code for the event handler contextMenuEvent() in the QTableWidget example

The context menu is displayed using exec(). The value that it returns, action, can be used to determine if the additional actions were clicked on in the context menu. We pass self.mapToGlobal() as an argument to get the coordinates of the mouse within the screen. The position of the mouse is determined with event.pos().

If action is equal to copy_act, we’ll call the method copyItem(). For paste_act, the pasteItem() method is called. These are created in Listing 10-13.

Using Built-in QTableWidget Methods to Edit Data

The remaining listings will create the different slots and methods in MainWindow. For Listing 10-12, we’ll create changeHeader() that is triggered when a column header is double-clicked.

To get the text for the selected column header, QInputDialog is displayed to get the header label text from the user. Finally, the item for the horizontal header is set using setHorizontalHeaderItem().
# table_widget.py
    def changeHeader(self):
        """Change horizontal headers by returning the text
        from input dialog."""
        col = self.table_widget.currentColumn()
        text, ok = QInputDialog.getText(
            self, "Enter Header", "Header text:")
        if ok and text != "":
            self.table_widget.setHorizontalHeaderItem(
                col, QTableWidgetItem(text))
Listing 10-12

Code for the changeHeader() slot in the QTableWidget example

Setting horizontal header labels can be accomplished with either setHorizontalHeaderItem() or setHorizontalHeaderLabels(). You can change Horizontal to Vertical in the method calls for vertical headers.

We’ll handle the extra methods in the context menu next in Listing 10-13. If the selected cell is not empty, we copy the text to item_text. In the pasteItem() method, the current row and column of the selected cell are collected. We then paste the data using setItem(). The copy and paste actions could also be implemented using the QClipboard.
# table_widget.py
    def copyItem(self):
        """If the current cell selected is not empty,
        store the text."""
        if self.table_widget.currentItem() != None:
            text = self.table_widget.currentItem().text()
            self.item_text = text
    def pasteItem(self):
        """Set item for selected cell."""
        if self.item_text != None:
            row = self.table_widget.currentRow()
            column = self.table_widget.currentColumn()
            self.table_widget.setItem(
                row, column, QTableWidgetItem(self.item_text))
Listing 10-13

Code for the copyItem() and pasteItem() methods used by the context menu

You can also add items to the table programmatically using the setItem() method . This allows you to specify the row and column values, and an item for the cell using QTableWidgetItem. In the following code, the item Kalani is inserted in row 0 and column 0.
        self.table_widget.setItem(
            1, 0, QTableWidgetItem("Kalani"))
QTableWidget includes a few methods for manipulating table objects. The Table menu creates actions that put those methods to use. These actions call slots that utilize built-in QTableWidget methods. The following list describes how these methods are used in the GUI and in Listing 10-14:
  • Adding rows above or below the currently selected row using insertRow()

  • Adding columns before or after the currently selected column using insertColumn()

  • Deleting the current row or column using removeRow() or removeColumn()

  • Clearing the entire table, including items and headers with clear()

# table_widget.py
    def addRowAbove(self):
        current_row = self.table_widget.currentRow()
        self.table_widget.insertRow(current_row)
    def addRowBelow(self):
        current_row = self.table_widget.currentRow()
        self.table_widget.insertRow(current_row + 1)
    def addColumnBefore(self):
        current_col = self.table_widget.currentColumn()
        self.table_widget.insertColumn(current_col)
    def addColumnAfter(self):
        current_col = self.table_widget.currentColumn()
        self.table_widget.insertColumn(current_col + 1)
    def deleteRow(self):
        current_row = self.table_widget.currentRow()
        self.table_widget.removeRow(current_row)
    def deleteColumn(self):
        current_col = self.table_widget.currentColumn()
        self.table_widget.removeColumn(current_col)
    def clearTable(self):
        self.table_widget.clear()
Listing 10-14

Code for the slots that modify data in the QTableWidget example

Items in a table are accessed using their row and column values. We’ll first need to know which row or column is currently selected. For example, when add_row_above_act is clicked, it triggers a signal that calls addRowAbove(). We first find out the row that is selected using currentRow(). A new row is then inserted in the current row’s location, causing all other rows to move down. Methods that manipulate columns use the currentColumn() method.

The last program will introduce the QTreeWidget convenience class.

The QTreeWidget Class

The QTreeWidget class shares similarities with both QListWidget and QTableWidget. On the one hand, data items can be displayed in a list-like format similar to QListWidget. On the other hand, QTreeWidget can also display multiple columns of data, but not in a tabular format.

What sets QTreeWidget apart is how the class can visually represent the relationships between data in a tree-like structure. It is possible for an item in the tree to be the parent of other items.

The GUI that we will build in this section is shown in Figure 10-6.
Figure 10-6

A QTreeWidget is used to present data in a tree-like structure

Items added to QTreeWidget are created from QTreeWidgetItem. For the items in Figure 10-6, there will be two parent items for the types of fruit and a number of child items with icons that are organized under the parents.

Similar to QTableWidget, QTreeWidget also contains horizontal headers for each column. However, there are no vertical headers.

Sorting of column items is also possible with QTreeWidget.

Be sure to download the icons folder from GitHub before beginning this application.

Explanation for Using QTreeWidget

We’ll begin with basic_window.py script from Chapter 1, update the imports, and set the window’s minimum size and title for the MainWindow class in Listing 10-15.
# tree_widget.py
# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget,
    QTreeWidget, QTreeWidgetItem, QVBoxLayout)
from PyQt6.QtGui import QIcon
class MainWindow(QWidget):
    def __init__(self):
        """ Constructor for Empty Window Class """
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(500, 300)
        self.setWindowTitle("QTreeWidget Example")
        self.setUpMainWindow()
        self.show()
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())
Listing 10-15

Setting up the MainWindow class for the QTreeWidget example

After creating the QTreeWidget object in Listing 10-16, we’ll need to set the number of columns with the setColumnCount() setter. Next, we’ll specify the labels for each of the columns. The method setHeaderLabels() takes an iterable object as an argument. The method setColumnWidth() is used to set the minimum column width of a specified column, ensuring that all of the items are clearly displayed. Here, column 0 is set to a width of 160 pixels.
# tree_widget.py
    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        tree_widget = QTreeWidget()
        tree_widget.setColumnCount(2)
        tree_widget.setHeaderLabels(
            ["Fruit Type", "Description"])
        tree_widget.setColumnWidth(0, 160)
        category_1 = QTreeWidgetItem(tree_widget, ["Apples",
            "Edible fruit produced by an apple tree"])
        apple_list = [
            ["Braeburn", "Yellow with red stripes or blush",
                "icons/braeburn.png"],
            ["Empire", "Solid red", "icons/empire.png"],
            ["Ginger Gold", "Green-yellow",
                "icons/ginger_gold.png"]]
        for i in range(len(apple_list)):
            category_1_child = QTreeWidgetItem(
                apple_list[i][:2])
            category_1_child.setIcon(
                0, QIcon(apple_list[i][2]))
            category_1.addChild(category_1_child)
Listing 10-16

Creating the setUpMainWindow() method for the QTreeWidget example, part 1

The category_1 instance is a QTreeWidgetItem for the first parent item in the tree. The parent widget, tree_widget, as well as a list of the information for the two columns is passed to the item. Following that, apple_list is a list of lists. Each list contains an apple type corresponding to the first column, a description to be displayed in the second column, and an icon that is displayed next to the item’s name. Each category_1_child is then turned into a QTreeWidgetItem, and its icon is set and at last added to the category_1 parent item in a Python for loop.

The same process is followed to create the second parent item and its children in Listing 10-17.
# tree_widget.py
        category_2 = QTreeWidgetItem(tree_widget,
            ["Oranges", "A type of citrus fruit"])
        orange_list = [
            ["Navel", "Sweet and slightly bitter",
                "icons/navel.png"],
            ["Blood Orange", "Juicy and tart",
                "icons/blood_orange.png"],
            ["Clementine", "Usually seedless",
                "icons/clementine.png"]]
        for i in range(len(apple_list)):
            category_2_child = QTreeWidgetItem(
                orange_list[i][:2])
            category_2_child.setIcon(
                0, QIcon(orange_list[i][2]))
            category_2.addChild(category_2_child)
        main_v_box = QVBoxLayout()
        main_v_box.addWidget(tree_widget)
        self.setLayout(main_v_box)
Listing 10-17

Creating the setUpMainWindow() method for the QTreeWidget example, part 2

With the items created, tree_widget is added to the layout of the main window.

With this GUI complete, you have now experienced creating applications for each of the Model/View convenience classes.

Summary

In this chapter, we took a look at the item-based convenience classes that follow standard methods for presenting data. Items are typically presented in a list, in a table, or in a tree. We learned about QListWidget, QTableWidget, and QTreeWidget and discovered how to use some of their features to create unique and practical GUIs. All of the item-based widget classes as well as many other classes that inherit QWidget have drag-and-drop capabilities. This system is very useful since it makes moving data between graphical elements in a GUI even simpler.

In the next chapter, we’ll explore the graphical aspects of GUIs and begin to see how we can add animation and color to applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset