2
votes

I'm trying to make a program that has multiple QTreeWidget's. However, my goal is to ONLY allow for 1 QTreeWidget row to be selected at a time.

I've kind of managed to make this happen using the currentItemChanged signal, but it bugs out.

To reproduce the issue...

  1. Select a row on one QTreeWidget.
  2. Select another row on the OTHER QTreeWidget. At this point all QTreeWidget selection are removed (great that's exactly what I wanted).
  3. Select the same row on the same QTreeWidget as step 1. At this point it has now bugged out because your previous selection is still chosen...

Am I clearing the selection improperly, and if so how should I be clearing it?

Here's a picture of the issue.

https://i.imgur.com/e4Uj5tb.png

Here is my source...

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTreeWidget>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void OnSelectedTreeValueChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QTreeWidget>
#include <QTreeWidgetItem>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->treeWidget->expandAll();
    ui->treeWidget->setItemsExpandable(false);

    ui->treeWidget_2->expandAll();
    ui->treeWidget_2->setItemsExpandable(false);

    connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(OnSelectedTreeValueChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
    connect(ui->treeWidget_2, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(OnSelectedTreeValueChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
}

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

void MainWindow::OnSelectedTreeValueChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
    if(current->childCount() == 0)
    {
        // Get OUR parrent item.
        QTreeWidgetItem* parent_item = current->parent();

        // Go through each tree & deselect selections.
        QObject* sender_object = sender();
        QTreeView* sender_tree = static_cast<QTreeView*>(sender_object);
        const QList<QTreeWidget*> children = ui->TreeScrollAreaContents->findChildren<QTreeWidget*>(QRegularExpression(), Qt::FindDirectChildrenOnly);
        for(QList<QTreeWidget*>::const_iterator it = children.begin(); it != children.end(); it++)
        {
            QTreeWidget* child_tree = *it;
            if(sender_tree != child_tree)
            {
                child_tree->clearSelection();
                child_tree->clearFocus();
            }
        }
    }
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>176</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QScrollArea" name="TreeScrollArea">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>401</width>
      <height>171</height>
     </rect>
    </property>
    <property name="widgetResizable">
     <bool>true</bool>
    </property>
    <widget class="QWidget" name="TreeScrollAreaContents">
     <property name="geometry">
      <rect>
       <x>0</x>
       <y>0</y>
       <width>399</width>
       <height>169</height>
      </rect>
     </property>
     <property name="styleSheet">
      <string notr="true">#TreeScrollAreaContents {
    background-color: #000000;
}</string>
     </property>
     <layout class="QGridLayout" name="TreeScrollAreaGridLayout">
      <property name="leftMargin">
       <number>0</number>
      </property>
      <property name="rightMargin">
       <number>0</number>
      </property>
      <item row="0" column="0">
       <widget class="QTreeWidget" name="treeWidget">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="sizeAdjustPolicy">
         <enum>QAbstractScrollArea::AdjustToContents</enum>
        </property>
        <column>
         <property name="text">
          <string>1</string>
         </property>
        </column>
        <item>
         <property name="text">
          <string>Item 1</string>
         </property>
         <item>
          <property name="text">
           <string>Sub Item 1</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 2</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 3</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 4</string>
          </property>
         </item>
        </item>
       </widget>
      </item>
      <item row="0" column="1">
       <widget class="QTreeWidget" name="treeWidget_2">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="sizeAdjustPolicy">
         <enum>QAbstractScrollArea::AdjustToContents</enum>
        </property>
        <column>
         <property name="text">
          <string>1</string>
         </property>
        </column>
        <item>
         <property name="text">
          <string>Item 1</string>
         </property>
         <item>
          <property name="text">
           <string>Sub Item 1</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 2</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 3</string>
          </property>
         </item>
         <item>
          <property name="text">
           <string>Sub Item 4</string>
          </property>
         </item>
        </item>
       </widget>
      </item>
     </layout>
    </widget>
   </widget>
  </widget>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

main.cpp

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}
1

1 Answers

1
votes

One thing is the currentItem and another is the selected items, instead of using the currentItemChanged signal you must use the signal itemSelectionChanged. When using clearSelection() the respective QTreeWidget will also emit the signal itemSelectionChanged and this could generate an infinite loop, the solution is to use blockSignals().

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTreeWidgetItem>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    Ui::MainWindow *ui;
private slots:
    void OnSelectedTreeValueChanged(); // remove arguments
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTreeWidget>
#include <QTreeWidgetItem>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    const QList<QTreeWidget*> childrens = ui->TreeScrollAreaContents->findChildren<QTreeWidget*>(QRegularExpression(), Qt::FindDirectChildrenOnly);
    for(QTreeWidget* child_tree: childrens)
    {
        child_tree->expandAll();
        child_tree->setItemsExpandable(false);
        connect(child_tree, &QTreeWidget::itemSelectionChanged, this, &MainWindow::OnSelectedTreeValueChanged);
    }
}

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

void MainWindow::OnSelectedTreeValueChanged()
{
    QTreeWidget *sender_tree = qobject_cast<QTreeWidget *>(sender());

    if(sender_tree->currentItem()->childCount() == 0)
    {
        const QList<QTreeWidget*> childrens = ui->TreeScrollAreaContents->findChildren<QTreeWidget*>(QRegularExpression(), Qt::FindDirectChildrenOnly);
        for(QTreeWidget* child_tree: childrens)
        {
            if(sender_tree != child_tree)
            {
                child_tree->blockSignals(true);
                child_tree->clearSelection();
                child_tree->blockSignals(false);
            }
        }
    }
}

Note:

I will explain why your method does not work, you are using the currentItem as a basic element of your algorithm, but a currentItem may be selected or no, clearSelection does not affect the currentItem.