Complete introduction to the usage of qtreewidget and QDockWidget (case analysis)

This section describes the use of QTreeWidget and QDockWidget, as well as the method of displaying pictures with QLabel. The instance samp4  QTreeWidget uses QTreeWidget as the main component to create a photo manager. The interface of the instance runtime is shown in Figure 1.



Figure 1 example samp4 8 runtime interface


This example mainly demonstrates the use of the following components.

  • QTreeWidget tree component: the QTreeWidget class is a class that creates and manages the tree structure. The instance uses a QTreeWidget component to manage the photo directory. Nodes can be added or deleted. Each node has a custom type set. In addition, a custom data is set. The picture node stores the full file name so that the picture can be displayed when the node is clicked.
  • QDockWidget docking area component: QDockWidget is an interface component that can be docked in the QMainWindow window or floating on the top of the desktop. In this instance, a QTreeWidget component is placed on the QDockWidget area, and it can be docked or floated on the left or right side of the main window.
  • Display picture of QLabel component: on the right side is a QScrollArea component. Place a QLabel component on the ScrollArea, and display the picture by setting a QPixmap for QLabel. Through QPixmap operation, the display can be zoomed in and out, including zooming in, zooming out, actual size, suitable width, suitable height, etc.

Interface design

Interface layout design

The main window of sample samp4  8 is inherited from QMainWindow. The interface is designed visually. The program function is mainly realized by Action. The main menu and main toolbar are also realized by it.

On the left side of the workspace is a QDockWidget component. Place a QTreeWidget component on the DockWidget, and fill the docked area with treewidges with horizontal layout.

On the right side of the workspace is a QScrollArea component. A QLabel component is placed in the QScrollArea component. The picture is displayed using the pixmap attribute of QLabel. The components inside the scrollArea adopt the horizontal layout. When the picture is small, the picture displayed by the Label can automatically reside in the middle of the scrollArea. When the picture displayed by the Label exceeds the size of the scrollArea, the scrollArea will automatically display the horizontal or vertical scroll bars to display the larger area.

After the ScmllArea component is set as the central component of the main window workspace in the main window constructor, a split bar will automatically appear between the DockWidget and the ScrollArea, which can split the size of the two components.

QDockWidget component property settings

Set the main properties of the DockWidget component in the UI designer. The main properties are as follows:

  • The allowedAreas property, which sets the allowed docking area.
    By setallowedareas( Qt :: dockwidgetareas) set the allowed docking area. The parameter areas is a combination of the enumeration type QT:: dockwidgetareas. You can set to dock on the left, right, top and bottom of the window, or all areas can be docked, or not allowed to dock.

    This instance is set to allow left and right docking.

  • features property to set the properties of the dock component.
    The setfeatures (dockwidgetfeatures) function is used to set the properties of the dock component. The parameter features is a combination of the values of the enumeration type QDockWidget::DockWidgetFeature. The enumeration values are as follows:
    • QDockWidget::DockWidgetClosable: the docking area can be closed.
    • Qdockwidget:: dockwidget movable: the docking area is movable.
    • QDockWidget::DockWidgetFloatable: dockable.
    • Qdockwidget:: dockwidget verticaltitlebar: displays the vertical title bar on the left side of the docking area.
    • Qdockwidget:: alldockwidget features: use all of the above features.
    • Qdockwidget:: nodockwidget features: cannot dock, move, and close.

    This instance can be closed, docked and floated.

Settings of QTreeWidget component

In the UI designer, double-click the QTreeWidget component on the interface to open the designer shown in Figure 2. There are two pages in the designer to design Columns and Items respectively.



Figure 2 designer of qtreewidget component (Items page)


The Columns page is used to design the columns of the tree, which can have multiple columns. In the designer, you can add, delete, move columns, and set the text, font, foreground color, background color, text alignment, and icon of columns. In this instance, two columns are set, with the titles of "node" and "node type".

The Items page is used to design the nodes of the directory tree. You can set attributes for each node, such as text, font, icon, etc., especially the flags attribute. You can set whether the node is optional, editable, CheckBox, etc., and you can also set the CheckState of the node. At the bottom of Figure 2 is a set of buttons to add a node, add a child node, delete a node, change the node level, level move a node, and so on.

Using designer to design columns and nodes of the directory tree is suitable for creating fixed structure directory tree, but the directory tree is generally created dynamically according to the content, so it needs to use code to create nodes.

Action design

Most of the function codes in this example are implemented by action. Action is designed in Action Editor, and then the main menu and toolbar are designed by action. The completed action is shown in Figure 3.



Figure 3 design Action

QTreeWidget operation

Tree node operation rules of this instance

The tree node operations in this instance are defined as follows:

  • The nodes of the directory tree are divided into three types: top level node, group node and picture node.
  • When a window is created, it initializes the directory tree, which has only one top-level node. This top-level node cannot be deleted, and it is not allowed to create a new top-level node.
  • Group nodes and picture nodes can be added under the top-level nodes.
  • Group nodes and picture nodes can be added under group nodes, and the number of group nodes is unlimited.
  • The picture node is a terminal node. You can add another picture node at the same level of the picture node.
  • When each node is created, its type information is set, and the picture node stores its full file name as custom data.
  • When you click a picture file node, the picture of its associated file is displayed.


In order to facilitate the implementation of the code later, the user-defined content added in the main window class, MainWindow, is listed first. The code is as follows (the functions of these enumeration types, variables and functions will be introduced in detail later):

class MainWindow : public QMainWindow
{
private:
    //Enumeration type treeItemType, used to create qtreewidgettitem as the type of node, custom type must be greater than 1000
    //itTopItem top level node; itGroupItem group node; itImageItem picture
    enum    treeItemType{itTopItem=1001,itGroupItem,itImageItem};
    //Enumeration type, indicating column number
    enum    treeColNum{colItem=0, colItemType=1}; //Number definition of tree columns
    QLabel  *LabFileName;
    QPixmap curPixmap; //Current picture
    float   pixRatio;//Current picture zoom
    void    iniTree();//Tree initialization
    void    addFolderItem(QTreeWidgetItem *parItem, QString dirName);//Add a directory node
    QString getFinalFolderName(const QString &fullPathName);//Get the last folder name from the full directory name
    void    addImageItem(QTreeWidgetItem *parItem,QString aFilename);//Add a picture node
    void    displayImage(QTreeWidgetItem *item); //Show a picture of a picture node
    //Void changecheckable (qtreewidgettitem * item, bool chk); / / change the checkable state of the node
    void    changeItemCaption(QTreeWidgetItem *item); //Traverse change node title
}

Tree initialization add top node

The main window's main window's constructor will call the custom function iniTree() to initialize the directory tree. The window constructor and iniTree() codes are as follows:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    LabFileName=new QLabel("");
    ui->statusBar->addWidget(LabFileName);
    this->setCentralWidget(ui->scrollArea);
    iniTree () ; //Initialize tree
}
void MainWindow::iniTree()
{ //Initialize Tree
    //Qtreewidgettitem * item; / / node
    QString dataStr=""; // string of Item's Data store
    ui->treeFiles->clear();//Clear all nodes in the tree
    QIcon icon;
    icon.addFile(":/images/icons/15.ico"); //ICON for setting ICON
    QTreeWidgetItem*  item=new QTreeWidgetItem(MainWindow::itTopItem); //Set the type to itTopItem when creating a new node
    item->setIcon(MainWindow::colItem,icon); //Set icon for column 1
    item->setText(MainWindow::colItem,"Picture file"); //Set text for column 1
    item->setText(MainWindow::colItemType,"type=itTopItem");  //Set text for column 2
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate);
    item->setCheckState(colItem,Qt::Checked);//Set to selected
    item->setData(MainWindow::colItem,Qt::UserRole,QVariant(dataStr)); //Set the Data of Qt::UserRole in column 1 of the node
    ui->treeFiles->addTopLevelItem(item);//Add top level node
}

Each node of QTreeWidget is a QTreeWidget item object. Before adding a node, you need to create it and make relevant settings.

The statement to create a node is:

item=new QTreeWidgetItem(MainWindow::itTopItem);

An enumeration constant MainWindow::itTopItem was passed as a parameter of the constructor to represent the type of the node. After passing a type value in the constructor, you can use qtreewidgettitem:: type() to return the type value of this node.

itTopItem is a constant value of the enumeration type treeltemType defined in MainWindow. Enumeration type treeItemType defines the type of node. The custom node type value must be greater than 1000.

Both setIcon() and setText() of qtreewidgettitem need to pass a column number as parameters to specify which column to set. The column number can be directly used as a number, but for the convenience of understanding the code and unified modification, the enumeration type treecolnum is defined in MainWindow, the colitem represents the first column, and the colItemType represents the second column.

setFlags() function sets some attribute tags of the node, which is a combination of constant of type Qt::ItemFlag enumeration.

The setData() function sets a role data for a column of a node. The prototype of the setData() function is:

void QTreeWidgetltem::setData(int column, int role, const QVariant &value)

Where column is the column number, role is the value of the role, and value is a number of QVariant type.

The statement to set node data in the code is:

item->setData(MainWindow::colItem,Qt::UserRole,QVariant(dataStr));

It sets a string data dataStr for column 1 of the node, role qtruserole. QtUserRole is a predefined value in the enumeration type Qt::ItemDataRole. The role of the node and Qt::ItemDataRole will be described in the following chapters.

After the node is created and set, use the QTreeWidget::addTopLevelItem() function to add the node to the directory tree as the top-level node.

Add directory node

actAddFolder is an Action used to add a group node. When the current node type on the directory tree is itTopItem or itGroupItem, you can add a group node. The code of the slot function of the triggered() signal of actAddFolder and the related custom function is as follows:

void MainWindow::on_actAddFolder_triggered()
{// Select a folder to join as a child of the current node
    QString dir=QFileDialog::getExistingDirectory();//Select directory
    if (!dir.isEmpty()) //The selected directory name is not empty
    {
        //Qtreewidgettitem * paritem; / / node
        QTreeWidgetItem* parItem=ui->treeFiles->currentItem(); //Current node
        addFolderItem(parItem,dir);//Add a group node under the parent node
    }
}
void MainWindow::addFolderItem(QTreeWidgetItem *parItem, QString dirName)
{//Add a directory node
    QIcon   icon(":/images/icons/open3.bmp");
     //icon.addFile(":/images/icons/open3.bmp"); / / set ICON of ICON
    QString NodeText=getFinalFolderName(dirName); //From a full directory name, get the last folder name
    QTreeWidgetItem *item; //node
    item=new QTreeWidgetItem(MainWindow::itGroupItem); //Create a new node and set the type to itGroupItem
    item->setIcon(colItem,icon); //Set icon
    item->setText(colItem,NodeText); //Last folder name, column 1
    //Item - > settext (colsecond, "type = itgroupitem; data =" + dirname); / / full directory name
    item->setText(colItemType,"type=itGroupItem"); //Full catalog name, column 2
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate); //Set node options
    item->setCheckState(colItem,Qt::Checked); //Node selection
    item->setData(colItem,Qt::UserRole,QVariant(dirName)); //Set the Data with the role Qt::UserRole to store the full directory name
    parItem->addChild(item); //Add child under Parent
}
QString MainWindow::getFinalFolderName(const QString &fullPathName)
{//From a full directory name, get the last folder name
    int cnt=fullPathName.length(); //String length
    int i=fullPathName.lastIndexOf("/");//  Last location
    QString str=fullPathName.right(cnt-i-1); //Get the name of the last folder
    return str;
}

actAddFolder's slot function first obtains a directory name with the file dialog box, then gets the current node of the directory tree, then calls the custom function addFolderItem() to add a group node, and the new added node will be the child node of the current node.

The addFolderItem() function creates and adds a node based on the passed parent node parItem and the full directory name dirName. First, use the custom function getFinalFolderName() to get the last folder name of the full name of the directory. The folder name will be the title of the new node; then create a node, set up its node type itGmnpItem, represent the grouping node, set the attribute and associated data, and the associated data is the full path string of the directory. The QTreeWidgetltem::addChild() function adds the created node to the directory tree as a child of the parent node.

Add picture file node

actAddFiles is the Action to add a picture file node. This Action is available when the current node of the directory tree is of any type. The code of the slot function of actAddFiles and related custom functions is as follows:

void MainWindow::on_actAddFiles_triggered()
{//Add picture file node
    QStringList files=QFileDialog::getOpenFileNames(this,"Select one or more files","","Images(*.jpg)");//Multiple selection file
    if (files.isEmpty()) //If you don't select a single file
        return;
    QTreeWidgetItem *parItem,*item; //node
    item=ui->treeFiles->currentItem(); //Current node
    if (item->type()==itImageItem) //If the current node is a picture node, take its parent node as the parent node
       parItem=item->parent();
    else //Otherwise, the current node is taken as the parent node
       parItem=item;
    for (int i = 0; i < files.size(); ++i)
    {
        QString aFilename=files.at(i); //Get a line in the StringList, which is a filename
        addImageItem(parItem,aFilename); //Add a picture node
    }
}
void MainWindow::addImageItem(QTreeWidgetItem *parItem, QString aFilename)
{//Add a picture file node
    QIcon   icon(":/images/icons/31.ico");//ICON for ICON
    QString NodeText=getFinalFolderName(aFilename); //Get the last file name
    QTreeWidgetItem *item; //node
    item=new QTreeWidgetItem(MainWindow::itImageItem); //Set the type to itImageItem when creating a new node
    item->setIcon(colItem,icon); //Set icon
    item->setText(colItem,NodeText); //Last folder name
    //Item - > settext (colsecond, "type = itimageitem; data =" + filename); / / full directory name
    item->setText(colItemType,"type=itImageItem"); //Full catalog name
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate); //Set node options
    item->setCheckState(colItem,Qt::Checked); //Node selection
    item->setData(colItem,Qt::UserRole,QVariant(aFilename)); //Set the Data of node Qt::UserRole to store the full file name
    parItem->addChild(item); //Add child under Parent
}

The slot function of actAddFiles first uses QFileDialog::getOpenFileNames() to get the list of picture files, and uses the QTreeWidget::currentItem() function to get the current node item of the directory tree.

Item - > type () returns the type of the node, which is the parameter passed to the constructor when the node is created. If the current node type is an itlmageltem, the parent node of the current node is used as the parent node of the picture node to be added. Otherwise, the current node is used as the parent node.

Then traverse the list of selected picture files, call the custom function addlmageltem() to add picture nodes to the parent node one by one. addImageItem() creates a node according to the picture file name and adds it under the parent node. When setData() is used to set the node data, the filename filename with path of the picture is used as the data of the node. This data will be used when clicking the node to open the picture.

Response after current node changes

When the current node changes on the directory tree, the currentItemChanged() signal will be sent. For this signal, a slot function will be created to judge the current node type, enable several actions to control, display pictures and other functions. The code is as follows:

void MainWindow::on_treeFiles_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{ //Triggered when the current node selection changes
    Q_UNUSED(previous);
    if  (current==NULL)
        return;
    int var=current->type();//Type of node
    switch(var)
    {
        case  itTopItem: //top node
          ui->actAddFolder->setEnabled(true);
          ui->actAddFiles->setEnabled(true);
          ui->actDeleteItem->setEnabled(false);    //Top level node cannot be deleted
          break;
        case  itGroupItem: //Filegroup node
          ui->actAddFolder->setEnabled(true);
          ui->actAddFiles->setEnabled(true);
          ui->actDeleteItem->setEnabled(true);
          break;
        case  itImageItem: //Picture file node
          ui->actAddFolder->setEnabled(false); //Cannot add directory node under picture node
          ui->actAddFiles->setEnabled(true);
          ui->actDeleteItem->setEnabled(true);
          displayImage(current); //display picture
          break;
    }
}

Current is the current node after change. Get the type of current node through current - > type(). Control the enabling status of three actions on the interface according to the node type. If it is a picture file node, the display image() function is also called to display the pictures associated with the node.

The function implementation of the displayImage() function will be described in detail later in the part of QLabel picture display.

Delete node

In addition to the top-level node, you can delete a node after selecting it. actDdeteItem implements node deletion. The code is as follows:

void MainWindow::on_actDeleteItem_triggered()
{//Delete node
    QTreeWidgetltem* item =ui->treeFiles->currentItem () ; //Current node
    QTreeWidgetltem* parItem=item->parent () ; //Parent node
    parItem->removeChild(item) ;//Removing a child node does not delete it
    delete item;
}

A node cannot remove itself, so you need to get its parent node and use the removeChild() function of the parent node to remove itself. Removechild () removes a node, but does not remove it from memory, so delete needs to be called as well.

To delete the top-level node, use the QTreeWidget::takeTopLevelItem(int index) function.

Traversal of nodes

The nodes of the directory tree are all qtreewidgettitem classes, which can be nested in multiple layers. Sometimes it is necessary to traverse all nodes in the directory tree, such as finding some nodes according to conditions, uniformly modifying the node title, etc. Some key functions of the qtreewidgettitem class and nested functions need to be designed for traversing nodes.

actScanItems implements the function of "traversing nodes" on the toolbar. Its slot function and related custom function codes are as follows:

void MainWindow::on_actScanItems_triggered()
{//Traversing node
    //QTreeWidgetItem *Item;
    for (int i=0;i<ui->treeFiles->topLevelItemCount();i++)
    {
        QTreeWidgetItem *item=ui->treeFiles->topLevelItem(i); //top node
        changeItemCaption(item); //Change node title
    }
}
void MainWindow::changeItemCaption(QTreeWidgetItem *item)
{ //Change the title text of the node
    QString str="*"+item->text(colItem);  //Add "*" before node title
    item->setText(colItem,str); //Set node title
    if (item->childCount()>0) //If there are child nodes
    for (int i=0;i<item->childCount();i++) //Traverse child nodes
       changeItemCaption(item->child(i));  //Call yourself, reentrant function
}

The top-level node of QTreeWidget component has no parent node. To access all the top-level nodes, two functions are used.

  1. inttopLevelItemCount(): returns the number of top-level nodes.
  2. QTreeWidgetltem* topLevelItem(int index): returns the top-level node whose sequence number is index. After getting a top-level node item, call changeltemCaption(item) to change the title of this node and all its children.


changeItemCaption(QTreeWidgeItem *item) is a nested calling function, that is, it will also call itself in this function. Its first two lines change the title of the node item passed in, which is preceded by an asterisk. The following code determines whether this node has child nodes according to whether the item - > childcount() is greater than 0. If there are child nodes, get them one by one in the following for loop, and call the changeItemCaption() function as a parameter.

The top-level node of QTreeWidget component has no parent node. To access all the top-level nodes, two functions are used.

  1. inttopLevelItemCount(): returns the number of top-level nodes.
  2. QTreeWidgetltem* topLevelItem(int index): returns the top-level node whose sequence number is index.


After getting a top-level node item, call changeltemCaption(item) to change the title of this node and all its children.

changeItemCaption(QTreeWidgetltem *item) is a nested calling function, that is, it will also call itself in this function. Its first two lines change the title of the node item passed in, which is preceded by an asterisk. The following code determines whether this node has child nodes according to whether the item - > childcount() is greater than 0. If there are child nodes, get them one by one in the following for loop, and call the changeItemCaption() function as a parameter.

QLabel and QPixmap display picture

Show pictures associated with nodes

After clicking a node in the directory tree, if its type is the image node (itlmageltem), the display image (qtreewidgetltem * item) function will be called to display the node's image, and the current node will be used as the transfer parameter of the function. The code of the displayImage() function is as follows:

void MainWindow::displayImage(QTreeWidgetItem *item)
{//Display picture, the name of the picture file stored in node item
    QString filename=item->data(colItem,Qt::UserRole).toString();//Get the file name stored in node data
    LabFileName->setText(filename);
    curPixmap.load(filename); //Load picture from file
    on_actZoomFitH_triggered(); //Auto fit height display
}

Qtreewidgettitem:: data() returns the data stored by the node, that is, the data set with setData(). Before, when adding a picture node, the full path name of the file name is stored as the data of the node. Here, the first line statement can obtain the full name of the picture file stored by the node.

curPixmap is a QPixmap type variable defined in MainWindow for manipulating pictures. QPixmap:: load (qstring & filename) loads a picture file directly.

Finally, calling function on_actZoomFitH_triggered() is not a picture, which is a slot function of actZoomFitH to display pictures in a high form.

Picture zoom and display

There are several actions to achieve image zoom display, including fit width, fit height, zoom in, zoom out and actual size. Some slot function codes are as follows:

void MainWindow::on_actZoomFitW_triggered()
{ //Fit width display
    int w=ui->scrollArea->width()-20;//Get the height of scrollArea
    int realw=curPixmap.width();//Actual width of the original image
    pixRatio=float(w)/realw;//Current display scale, must be converted to floating point number
    QPixmap pix=curPixmap.scaledToWidth(w-30);
    ui->LabPicture->setPixmap(pix);
}
void MainWindow::on_actZoomIn_triggered()
{//Zoom display
    pixRatio=pixRatio*1.2;//Multiply the current scale by 0.8
    int w=pixRatio*curPixmap.width();// Display width
    int h=pixRatio*curPixmap.height();//Display height
    QPixmap pix=curPixmap.scaled(w,h);//Zoom the picture to the specified height and width, keeping the length width ratio
    ui->LabPicture->setPixmap(pix);
}
void MainWindow::on_actZoomRealSize_triggered()
{ //Actual size display
    pixRatio=1;  //Restore display scale to 1
    ui->LabPicture->setPixmap(curPixmap);
}

QPixmap stores picture data and can scale pictures. It has the following functions:

  • QPixmap scaledToHeight(int height): returns a copy of the zoomed image. The image is zoomed to a height.
  • QPixmap scaledToWidth(int width): returns a copy of the zoomed image. The image is zoomed to a width.
  • QPixmap scaled(int width, int height): returns a scaled copy of the picture. The picture is scaled to width width and height. The default is not to maintain the scale.


The variable curPixmap saves the original copy of the picture. To zoom, you only need to call the corresponding function of curPixmap to return the zoomed copy of the picture.

A label LabPicture on the interface does not show the picture. The setpixmap (const qpixmap &) function of QLabel is used.

Operation of QDockWidget

When the program is running, the DockWidget component on the main window can be dragged, docked on the left and right sides of the main window, or floated on the desktop. The two buttons "form floating" and "window visible" on the toolbar can use code to control whether the docking area is floating or visible. The code is as follows:

void MainWindow::on_actDockVisible_toggled(bool arg1)
{// Visibility of docking areas
    ui->dockWidget->setVisible(arg1);
}
void MainWindow::on_actDockFloat_triggered(bool checked)
{//Docking area FLOATABILITY
    ui->dockWidget->setFloating(checked);
}
//When you click the close button in the title bar of the DockWidget component, the docking area will be hidden and the signal visibilityChanged(bool) will be sent; when you drag the DockWidget component to float or dock it, the signal topLevelChanged(bool) will be sent. Write slot functions for the two signals to update the status of the two Actions:
void MainWindow::on_dockWidget_visibilityChanged(bool visible)
{//Changes in visibility of docking areas
    ui->actDockVisible->setChecked(visible);
}
void MainWindow::on_dockWidget_topLevelChanged(bool topLevel)
{//Floating change of docking area
    ui->actDockFloat->setChecked(topLevel);
}

 

Published 107 original articles, won praise 4, visited 7859
Private letter follow

Tags: Qt Attribute Asterisk

Posted on Wed, 12 Feb 2020 01:04:46 -0500 by abhi_10_20