Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Selecting QTreeView Switches Focus on Default QButton
Forum Updated to NodeBB v4.3 + New Features

Selecting QTreeView Switches Focus on Default QButton

Scheduled Pinned Locked Moved Unsolved General and Desktop
11 Posts 3 Posters 969 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    gsephelec
    wrote on last edited by
    #1

    Hi,

    I have a QTreeView and 2 x QButtons: Ok and Cancel (accept and reject). The default button is Ok but when I run my project on MacOS and I click the mouse anywhere within the QTreeView, the highlighted button switches from Ok to Cancel.

    I've simplified my code and managed to still get the issue:

    assetSelectLabel = new QLabel(label);
    
    assetSelectTree = new QTreeView();
    assetSelectTree->setFixedSize(260, 360);
    assetSelectTree->setHeaderHidden(true);
    assetSelectTree->setDragEnabled(false);
    assetSelectTree->setEditTriggers(QAbstractItemView::NoEditTriggers);
    
    assetSelectItemModel = new QStandardItemModel(this);
    
    // Data would usually be added here
    
    assetSelectTree->setModel(assetSelectItemModel);
    assetSelectTree->setCurrentIndex(assetSelectItemModel->invisibleRootItem()->index());
    assetSelectTree->expandAll();
    
    okButton = new QPushButton(QString("&OK"));
    okButton->setDefault(true);
    
    cancelButton = new QPushButton(QString("&Cancel"));
    
    buttonBox = new QDialogButtonBox(Qt::Horizontal);
    buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
    buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
    
    QVBoxLayout *assetSelectLayout = new QVBoxLayout;
    assetSelectLayout->addWidget(assetSelectLabel);
    assetSelectLayout->addWidget(assetSelectTree);
    
    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);
    mainLayout->addLayout(assetSelectLayout, 0, 0);
    mainLayout->addWidget(buttonBox, 1, 0);
    
    setLayout(mainLayout);
    
    connect(buttonBox, &QDialogButtonBox::accepted, this, &assetAssetSelector::slotAccept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &assetAssetSelector::slotReject);
    

    When the program runs, the Ok button is highlighted and hitting the Enter key calls the accept slot, but as soon as I click inside the QTreeView, either on an item or a blank space, the Cancel button is higlighted and hitting the Enter key calls the reject slot.

    I'm unsure what I need to change to ensure the Ok button continues to be highlighted.

    Thanks.

    1 Reply Last reply
    0
    • Axel SpoerlA Offline
      Axel SpoerlA Offline
      Axel Spoerl
      Moderators
      wrote on last edited by
      #2

      Which Qt Version are you running, and which OS is the one that works without switching the default button?

      Software Engineer
      The Qt Company, Oslo

      G 1 Reply Last reply
      0
      • Axel SpoerlA Axel Spoerl

        Which Qt Version are you running, and which OS is the one that works without switching the default button?

        G Offline
        G Offline
        gsephelec
        wrote on last edited by
        #3

        @Axel-Spoerl

        Qt 6.6.0 (Clang 13.0 (Apple), arm64)

        I can't verify at this time, but I don't think the same thing happens when I run the same code but on Windows.

        I tried adding the following code:

        assetSelectTree->setFocusPolicy(Qt::NoFocus);
        

        and this seems to resolve what I'm seeing, but I'm not sure if it's the correct way to do it?

        Axel SpoerlA 1 Reply Last reply
        0
        • G gsephelec

          @Axel-Spoerl

          Qt 6.6.0 (Clang 13.0 (Apple), arm64)

          I can't verify at this time, but I don't think the same thing happens when I run the same code but on Windows.

          I tried adding the following code:

          assetSelectTree->setFocusPolicy(Qt::NoFocus);
          

          and this seems to resolve what I'm seeing, but I'm not sure if it's the correct way to do it?

          Axel SpoerlA Offline
          Axel SpoerlA Offline
          Axel Spoerl
          Moderators
          wrote on last edited by Axel Spoerl
          #4

          @gsephelec
          Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
          MacOS treats buttons differently from other OSes at times, e.g. in a QDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
          A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
          Can you isolate the issue in a minimal, compilable reproducer?

          Software Engineer
          The Qt Company, Oslo

          G 2 Replies Last reply
          0
          • Axel SpoerlA Axel Spoerl

            @gsephelec
            Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
            MacOS treats buttons differently from other OSes at times, e.g. in a QDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
            A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
            Can you isolate the issue in a minimal, compilable reproducer?

            G Offline
            G Offline
            gsephelec
            wrote on last edited by
            #5

            @Axel-Spoerl

            Sure thing, I've uploaded a completely minimal project that recreates the issue here:

            https://github.com/george-sephton/QTreeView_Test

            1 Reply Last reply
            0
            • Axel SpoerlA Axel Spoerl

              @gsephelec
              Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
              MacOS treats buttons differently from other OSes at times, e.g. in a QDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
              A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
              Can you isolate the issue in a minimal, compilable reproducer?

              G Offline
              G Offline
              gsephelec
              wrote on last edited by
              #6

              @Axel-Spoerl

              I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.

              Could you reproduce the issue with the code I posted?

              Thanks

              JonBJ Axel SpoerlA 2 Replies Last reply
              0
              • G gsephelec

                @Axel-Spoerl

                I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.

                Could you reproduce the issue with the code I posted?

                Thanks

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by
                #7

                @gsephelec
                I'm throwing this thought in. No testing, and I don't have Mac anyway.

                I note that the the cancelButton is the first button you add to the QDialogButtonBox. I don't know why or whether this is desired/intended, but my suspicion is that it resets/selects the first button? Try swapping the order of your cancelButton/okButton, does it now select the okButton?

                G 1 Reply Last reply
                0
                • JonBJ JonB

                  @gsephelec
                  I'm throwing this thought in. No testing, and I don't have Mac anyway.

                  I note that the the cancelButton is the first button you add to the QDialogButtonBox. I don't know why or whether this is desired/intended, but my suspicion is that it resets/selects the first button? Try swapping the order of your cancelButton/okButton, does it now select the okButton?

                  G Offline
                  G Offline
                  gsephelec
                  wrote on last edited by
                  #8

                  @JonB

                  It seems to make no difference regardless of the order the buttons are added to the QDialogButtonBox unfortunately.

                  1 Reply Last reply
                  0
                  • G gsephelec

                    @Axel-Spoerl

                    I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.

                    Could you reproduce the issue with the code I posted?

                    Thanks

                    Axel SpoerlA Offline
                    Axel SpoerlA Offline
                    Axel Spoerl
                    Moderators
                    wrote on last edited by
                    #9

                    @gsephelec
                    I don't download reproducer code from external sources.
                    If you want me to reproduce, the code has to be so minimal that you can post it here.

                    Software Engineer
                    The Qt Company, Oslo

                    G 1 Reply Last reply
                    0
                    • Axel SpoerlA Axel Spoerl

                      @gsephelec
                      I don't download reproducer code from external sources.
                      If you want me to reproduce, the code has to be so minimal that you can post it here.

                      G Offline
                      G Offline
                      gsephelec
                      wrote on last edited by
                      #10

                      @Axel-Spoerl

                      See code here:

                      main.cpp

                      #include "mainwindow.h"
                      #include <QApplication>
                      
                      int main(int argc, char *argv[])
                      {
                          QApplication a(argc, argv);
                          MainWindow w;
                          w.show();
                          return a.exec();
                      }
                      

                      mainwindow.cpp

                      #include "mainwindow.h"
                      #include "assetDialog.h"
                      
                      MainWindow::MainWindow(QWidget *parent)
                          : QMainWindow(parent)
                      {
                          assetAssetSelector dialog(this);
                          dialog.exec();
                      }
                      
                      MainWindow::~MainWindow()
                      {
                      }
                      

                      mainwindow.h

                      #ifndef MAINWINDOW_H
                      #define MAINWINDOW_H
                      
                      #include <QMainWindow>
                      
                      #include <QtWidgets>
                      #include <QTreeView>
                      
                      
                      QT_BEGIN_NAMESPACE
                      namespace Ui {
                      class MainWindow;
                      }
                      QT_END_NAMESPACE
                      
                      class MainWindow : public QMainWindow
                      {
                          Q_OBJECT
                      
                      public:
                          MainWindow(QWidget *parent = nullptr);
                          ~MainWindow();
                      
                      private:
                          Ui::MainWindow *ui;
                      
                          QLabel *assetSelectLabel;
                          QTreeView *assetSelectTree;
                          QDialogButtonBox *buttonBox;
                          QPushButton *okButton;
                          QPushButton *cancelButton;
                      
                          QStandardItemModel *assetSelectItemModel;
                      };
                      #endif // MAINWINDOW_H
                      

                      assetDialog.cpp

                      #include "assetDialog.h"
                      
                      assetAssetSelector::assetAssetSelector(QWidget *parent)
                          : QDialog(parent)
                      {
                          assetSelectLabel = new QLabel("Test");
                      
                          assetSelectTree = new QTreeView();
                          assetSelectTree->setFixedSize(260, 360);
                          assetSelectTree->setHeaderHidden(true);
                          assetSelectTree->setDragEnabled(false);
                          assetSelectTree->setEditTriggers(QAbstractItemView::NoEditTriggers);
                      
                          assetSelectItemModel = new QStandardItemModel(this);
                      
                          // Data would usually be added here
                      
                          assetSelectTree->setModel(assetSelectItemModel);
                          assetSelectTree->setCurrentIndex(assetSelectItemModel->invisibleRootItem()->index());
                          assetSelectTree->expandAll();
                      
                          okButton = new QPushButton(QString("&OK"));
                          okButton->setDefault(true);
                      
                          cancelButton = new QPushButton(QString("&Cancel"));
                      
                          buttonBox = new QDialogButtonBox(Qt::Horizontal);
                          buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
                          buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
                      
                          QVBoxLayout *assetSelectLayout = new QVBoxLayout;
                          assetSelectLayout->addWidget(assetSelectLabel);
                          assetSelectLayout->addWidget(assetSelectTree);
                      
                          QGridLayout *mainLayout = new QGridLayout;
                          mainLayout->setSizeConstraint(QLayout::SetFixedSize);
                          mainLayout->addLayout(assetSelectLayout, 0, 0);
                          mainLayout->addWidget(buttonBox, 1, 0);
                      
                          setLayout(mainLayout);
                      
                          connect(buttonBox, &QDialogButtonBox::accepted, this, &assetAssetSelector::slotAccept);
                          connect(buttonBox, &QDialogButtonBox::rejected, this, &assetAssetSelector::slotReject);
                      }
                      
                      void assetAssetSelector::slotAccept()
                      {
                          /* Accept the dialog */
                          accept();
                      }
                      
                      void assetAssetSelector::slotReject()
                      {
                          /* Reject the dialog */
                          reject();
                      }
                      

                      assetDialog.h

                      #ifndef ASSETDIALOG_H
                      #define ASSETDIALOG_H
                      #pragma once
                      
                      #include "mainWindow.h"
                      
                      #include <QDialog>
                      #include <QtWidgets>
                      #include <QTreeView>
                      
                      #include <string>
                      
                      QT_BEGIN_NAMESPACE
                      class QLabel;
                      class QLineEdit;
                      class QComboBox;
                      //class QCheckBox;
                      
                      class QDialogButtonBox;
                      class QPushButton;
                      QT_END_NAMESPACE
                      
                      class assetAssetSelector : public QDialog
                      {
                          Q_OBJECT
                      
                      public:
                          assetAssetSelector(QWidget *parent = nullptr);
                      
                      protected slots:
                          void slotAccept();
                          void slotReject();
                      
                      private:
                          QLabel *assetSelectLabel;
                          QTreeView *assetSelectTree;
                          QDialogButtonBox *buttonBox;
                          QPushButton *okButton;
                          QPushButton *cancelButton;
                      
                          QStandardItemModel *assetSelectItemModel;
                      };
                      
                      #endif // ASSETDIALOG_H
                      

                      This repoduces the error on Macbook Pro Nov 2023 M3 Pro, Qt 6.6.0 (Clang 13.0 (Apple), arm64).

                      Thanks

                      Axel SpoerlA 1 Reply Last reply
                      0
                      • G gsephelec

                        @Axel-Spoerl

                        See code here:

                        main.cpp

                        #include "mainwindow.h"
                        #include <QApplication>
                        
                        int main(int argc, char *argv[])
                        {
                            QApplication a(argc, argv);
                            MainWindow w;
                            w.show();
                            return a.exec();
                        }
                        

                        mainwindow.cpp

                        #include "mainwindow.h"
                        #include "assetDialog.h"
                        
                        MainWindow::MainWindow(QWidget *parent)
                            : QMainWindow(parent)
                        {
                            assetAssetSelector dialog(this);
                            dialog.exec();
                        }
                        
                        MainWindow::~MainWindow()
                        {
                        }
                        

                        mainwindow.h

                        #ifndef MAINWINDOW_H
                        #define MAINWINDOW_H
                        
                        #include <QMainWindow>
                        
                        #include <QtWidgets>
                        #include <QTreeView>
                        
                        
                        QT_BEGIN_NAMESPACE
                        namespace Ui {
                        class MainWindow;
                        }
                        QT_END_NAMESPACE
                        
                        class MainWindow : public QMainWindow
                        {
                            Q_OBJECT
                        
                        public:
                            MainWindow(QWidget *parent = nullptr);
                            ~MainWindow();
                        
                        private:
                            Ui::MainWindow *ui;
                        
                            QLabel *assetSelectLabel;
                            QTreeView *assetSelectTree;
                            QDialogButtonBox *buttonBox;
                            QPushButton *okButton;
                            QPushButton *cancelButton;
                        
                            QStandardItemModel *assetSelectItemModel;
                        };
                        #endif // MAINWINDOW_H
                        

                        assetDialog.cpp

                        #include "assetDialog.h"
                        
                        assetAssetSelector::assetAssetSelector(QWidget *parent)
                            : QDialog(parent)
                        {
                            assetSelectLabel = new QLabel("Test");
                        
                            assetSelectTree = new QTreeView();
                            assetSelectTree->setFixedSize(260, 360);
                            assetSelectTree->setHeaderHidden(true);
                            assetSelectTree->setDragEnabled(false);
                            assetSelectTree->setEditTriggers(QAbstractItemView::NoEditTriggers);
                        
                            assetSelectItemModel = new QStandardItemModel(this);
                        
                            // Data would usually be added here
                        
                            assetSelectTree->setModel(assetSelectItemModel);
                            assetSelectTree->setCurrentIndex(assetSelectItemModel->invisibleRootItem()->index());
                            assetSelectTree->expandAll();
                        
                            okButton = new QPushButton(QString("&OK"));
                            okButton->setDefault(true);
                        
                            cancelButton = new QPushButton(QString("&Cancel"));
                        
                            buttonBox = new QDialogButtonBox(Qt::Horizontal);
                            buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
                            buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
                        
                            QVBoxLayout *assetSelectLayout = new QVBoxLayout;
                            assetSelectLayout->addWidget(assetSelectLabel);
                            assetSelectLayout->addWidget(assetSelectTree);
                        
                            QGridLayout *mainLayout = new QGridLayout;
                            mainLayout->setSizeConstraint(QLayout::SetFixedSize);
                            mainLayout->addLayout(assetSelectLayout, 0, 0);
                            mainLayout->addWidget(buttonBox, 1, 0);
                        
                            setLayout(mainLayout);
                        
                            connect(buttonBox, &QDialogButtonBox::accepted, this, &assetAssetSelector::slotAccept);
                            connect(buttonBox, &QDialogButtonBox::rejected, this, &assetAssetSelector::slotReject);
                        }
                        
                        void assetAssetSelector::slotAccept()
                        {
                            /* Accept the dialog */
                            accept();
                        }
                        
                        void assetAssetSelector::slotReject()
                        {
                            /* Reject the dialog */
                            reject();
                        }
                        

                        assetDialog.h

                        #ifndef ASSETDIALOG_H
                        #define ASSETDIALOG_H
                        #pragma once
                        
                        #include "mainWindow.h"
                        
                        #include <QDialog>
                        #include <QtWidgets>
                        #include <QTreeView>
                        
                        #include <string>
                        
                        QT_BEGIN_NAMESPACE
                        class QLabel;
                        class QLineEdit;
                        class QComboBox;
                        //class QCheckBox;
                        
                        class QDialogButtonBox;
                        class QPushButton;
                        QT_END_NAMESPACE
                        
                        class assetAssetSelector : public QDialog
                        {
                            Q_OBJECT
                        
                        public:
                            assetAssetSelector(QWidget *parent = nullptr);
                        
                        protected slots:
                            void slotAccept();
                            void slotReject();
                        
                        private:
                            QLabel *assetSelectLabel;
                            QTreeView *assetSelectTree;
                            QDialogButtonBox *buttonBox;
                            QPushButton *okButton;
                            QPushButton *cancelButton;
                        
                            QStandardItemModel *assetSelectItemModel;
                        };
                        
                        #endif // ASSETDIALOG_H
                        

                        This repoduces the error on Macbook Pro Nov 2023 M3 Pro, Qt 6.6.0 (Clang 13.0 (Apple), arm64).

                        Thanks

                        Axel SpoerlA Offline
                        Axel SpoerlA Offline
                        Axel Spoerl
                        Moderators
                        wrote on last edited by
                        #11

                        @gsephelec
                        Sorry for taking so long.

                        The setVisible()override of QDialogchecks, if there is a QPushButtonat the next position in the focus chain. If it finds one, it sets it as a default button. That matches almost all use cases. But it can get in the way of a QDialogButtonBoxinside a QDialog. In your case, the cancel button is next in the focus chain. Therefore it wins the prize and becomes default.

                        There is a simple workaround:
                        Define void setVisible(bool visible) override;in the (new) protectedsection of assetDialog.hand implement it as follows:

                        void assetAssetSelector::setVisible(bool visible)
                        {
                            QDialog::setVisible(visible);
                            okButton->setDefault(true);
                        }
                        

                        It's a bit hacky, but it does the job.

                        You can file a bugreport if you want - you have a perfect reproducer.
                        I can't promise if we will actually fix it. I somehow feel, that a fix in Qt would break a lot of other stuff.

                        Software Engineer
                        The Qt Company, Oslo

                        1 Reply Last reply
                        1

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved