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. Subclassing QFileDialog
Forum Updated to NodeBB v4.3 + New Features

Subclassing QFileDialog

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 7 Posters 1.3k Views 5 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.
  • PerdrixP Perdrix

    The MFC code that I am converting to use Qt uses a sub-class of CFileDialog to that uses CDialog::SetTemplate() function to append additional controls defined in a user resource file to the standard dialogue.

    The resulting save file dialogue looks like this:

    4dc05345-d87d-4da5-a8c1-34cb062a97d0-image.png

    This is very effective, but unfortunately I can't seem to find a way to do this using QFileDialog.

    Is there a way to achieve something similar to this in Qt?

    Thanks, David

    Pl45m4P Offline
    Pl45m4P Offline
    Pl45m4
    wrote on last edited by Pl45m4
    #2

    @Perdrix said in Subclassing QFileDialog:

    Is there a way to achieve something similar to this in Qt?

    QFileDialog inherits from QDialog.
    So you can subclass it and add your own layout with additional things you need.
    If you want to replicate the exact same as in your screenshot, there are two QGroupBox containers with check- and radio buttons... so should not be a big deal.

    Keep in mind, when you subclass QFileDialog you always get the Qt Widget "style" File Dialog and never the platform native one, which, for example, can be invoked using the static methods of QFileDialog.


    If debugging is the process of removing software bugs, then programming must be the process of putting them in.

    ~E. W. Dijkstra

    1 Reply Last reply
    2
    • PerdrixP Offline
      PerdrixP Offline
      Perdrix
      wrote on last edited by Perdrix
      #3

      Some guidance on that would be very helpful. It's not clear to me how I'd override its layout and add controls. The way that QFileDialogPrivate::createWidgets() works looks like this will be very difficult without copying the entire code of (at least) that function to override the layout.

      Meantime, I'll look deeper into its source code to see whether I can work it out for myself

      Thanks, David

      1 Reply Last reply
      0
      • Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #4

        QFileDialog is not really meant to be overriden. Create your own dialog if you want something special.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        0
        • PerdrixP Offline
          PerdrixP Offline
          Perdrix
          wrote on last edited by
          #5

          I find that really depressing - I so don't want to have to spin my own version of QFileDialog from the ground up.

          David

          Pl45m4P M 2 Replies Last reply
          0
          • PerdrixP Perdrix

            I find that really depressing - I so don't want to have to spin my own version of QFileDialog from the ground up.

            David

            Pl45m4P Offline
            Pl45m4P Offline
            Pl45m4
            wrote on last edited by
            #6

            @Perdrix

            But it's the same for every other widget.
            When subclassing e.g. QAbstractButton you get the interface only... the button is "empty" and needs to be defined and customized by you. Unfortunately there's nothing such as "a little default behavior" and then add your own stuff to it.


            If debugging is the process of removing software bugs, then programming must be the process of putting them in.

            ~E. W. Dijkstra

            1 Reply Last reply
            0
            • sbelaS Offline
              sbelaS Offline
              sbela
              wrote on last edited by
              #7

              QFileDialog is open source. Use that as a starting point.

              I would like!

              1 Reply Last reply
              1
              • PerdrixP Perdrix

                I find that really depressing - I so don't want to have to spin my own version of QFileDialog from the ground up.

                David

                M Offline
                M Offline
                mpergand
                wrote on last edited by mpergand
                #8

                @Perdrix

                According to the docs, I think its possible if not using the native file dialog.

                By default, a platform-native file dialog will be used if the platform has one. In that case, the widgets which would otherwise be used to construct the dialog will not be instantiated, so related accessors such as layout() and itemDelegate() will return null. You can set the DontUseNativeDialog option to ensure that the widget-based implementation will be used instead of the native dialog.

                The following add a button at the bottom.

                QFileDialog fd;
                fd.setOption(QFileDialog::DontUseNativeDialog);
                fd.layout()->addWidget(new QPushButton("Extra button"));
                
                1 Reply Last reply
                1
                • PerdrixP Perdrix has marked this topic as solved on
                • PerdrixP Offline
                  PerdrixP Offline
                  Perdrix
                  wrote on last edited by
                  #9

                  Woohoo!

                  image.png

                  SavePicture.h (part)

                  namespace DSS
                  {
                  	class SavePicture final : public QFileDialog
                  	{
                  		Q_OBJECT
                  
                  	public:
                  		SavePicture(QWidget* parent = nullptr, const QString& caption = QString(), const QString& directory = QString(), const QString& filter = QString());
                  
                  		~SavePicture() = default;
                  
                  		SavePicture(const SavePicture&) = delete;
                  		SavePicture(SavePicture&&) = delete;
                  		SavePicture& operator=(const SavePicture& rhs) = delete;
                  
                  		void retranslateUi(QWidget*);
                  
                  	private:
                  		QGroupBox* compressionGroup;
                  		QHBoxLayout* compressionLayout;
                  		QRadioButton* compressionNone;
                  		QRadioButton* compressionZIP;
                  		QRadioButton* compressionLZW;
                  
                  		QGroupBox* optionsGroup;
                  		QVBoxLayout* optionsLayout;
                  		QRadioButton* applyAdjustments;
                  		QRadioButton* embedAdjustments;
                  		QCheckBox* useRectangle;
                  
                  		QString embedText;
                  		QString noAdjustments;
                  

                  SavePicture.cpp (part)

                  namespace DSS
                  {
                  	SavePicture::SavePicture(QWidget* parent, const QString& caption, const QString& directory, const QString& filter) :
                  		QFileDialog(parent, caption, directory, filter),
                  		compressionGroup(new QGroupBox(this)),
                  		compressionLayout(new QHBoxLayout(compressionGroup)),
                  		compressionNone(new QRadioButton(compressionGroup)),
                  		compressionZIP(new QRadioButton(compressionGroup)),
                  		compressionLZW(new QRadioButton(compressionGroup)),
                  		optionsGroup(new QGroupBox(this)),
                  		optionsLayout(new QVBoxLayout(optionsGroup)),
                  		applyAdjustments(new QRadioButton(optionsGroup)),
                  		embedAdjustments(new QRadioButton(optionsGroup)),
                  		useRectangle(new QCheckBox(optionsGroup))
                  	{
                  		compressionGroup->setObjectName("compressionGroup");
                  		compressionLayout->setObjectName("compressionLayout");
                  		compressionNone->setObjectName("compressionNone");
                  		compressionZIP->setObjectName("compressionZIP");
                  		compressionLZW->setObjectName("compressionLZW");
                  
                  		optionsGroup->setObjectName("optionsGroup");
                  		optionsLayout->setObjectName("optionsLayout");
                  		applyAdjustments->setObjectName("applyAdjustments");
                  		embedAdjustments->setObjectName("embedAdjustments");
                  		useRectangle->setObjectName("useRectangle");
                  
                  		compressionGroup->setLayout(compressionLayout);
                  		compressionLayout->addWidget(compressionNone);
                  		compressionLayout->addWidget(compressionZIP);
                  		compressionLayout->addWidget(compressionLZW);
                  
                  		optionsGroup->setLayout(optionsLayout);
                  		optionsLayout->addWidget(applyAdjustments);
                  		optionsLayout->addWidget(embedAdjustments);
                  		optionsLayout->addWidget(useRectangle);
                  
                  		retranslateUi(this);
                  
                  		setOption(QFileDialog::DontUseNativeDialog);
                  
                  		QGridLayout* layout{ dynamic_cast<QGridLayout*>(this->layout()) };
                  		layout->addWidget(compressionGroup, layout->rowCount(), 0, 1, 2);
                  		layout->addWidget(optionsGroup, layout->rowCount(), 0, 1, 2);
                  	}
                  
                  	void SavePicture::retranslateUi([[maybe_unused]]QWidget* wdgt)
                  	{
                  		compressionGroup->setTitle(tr("Compression", "IDD_SAVEPICTURE"));
                  		compressionNone->setText(tr("None", "IDC_COMPRESSION_NONE"));
                  		compressionZIP->setText(tr("ZIP (Deflate)", "IDC_COMPRESSION_ZIP"));
                  		compressionLZW->setText(tr("ZIP (Deflate)", "IDC_COMPRESSION_LZW"));
                  		optionsGroup->setTitle(tr("Options", "IDD_SAVEPICTURE"));
                  		applyAdjustments->setText(tr("Apply adjustments to the saved image", "IDC_APPLIED"));
                  		embedAdjustments->setText(tr("Embed adjustments in the saved image but do not apply them", "IDC_EMBEDDED"));
                  		embedText = embedAdjustments->text();
                  		noAdjustments = tr("Do not apply adjustments to the saved image", "IDS_SAVENOADJUSTMENT");
                  		useRectangle->setText(tr("Create an image from the selected rectangle", "IDC_USERECT"));
                  	}```
                  1 Reply Last reply
                  4
                  • R Offline
                    R Offline
                    RoXus
                    wrote on last edited by
                    #10

                    This code probably works fine for saving dialogs, but (at least in my X11-environment) opening of a file does not, as the dialog closes as soon as a file is selected. But I found a solution by subclassing QFileDialog. The most important snippets of my Python-code are as follows:

                        def __init__(self, parent=None):
                            ...
                            # set custom dialog option
                            self.setOption(QFileDialog.DontUseNativeDialog)
                            # add your own widgets to the dialog
                            layg: QGridLayout = self.layout()
                            # the standard layout of QFileDialog is a QGridLayout with 4 rows and 3 columns; 
                            # so add your widgets from row 5 onwards
                            ...
                            # redirect accept signal from open button, which is a QDialogButtonBox in 
                            # row 2, column 2
                            itm_open = layg.itemAtPosition(2, 2)
                            itm_open.widget().accepted.connect(self.accept_button)      
                            self.show()
                    
                        def accept(self):
                            # override the standard accept function, which is triggered
                            # on file selection
                            logger.debug('accepted via file selection')
                            filename = self.selectedFiles()[0]
                            # I e.g. used the additional widgets to display parts of the contents 
                            # of the file; this code follows here
                            ...
                            # and IMPORTANT: do NOT call super().accept(), as this closes 
                            # the dialog immediately
                    
                        def accept_button(self):
                            # react on the click of the open button
                            logger.debug('accepted via button')
                            ...
                            # now it is time to call super().accept()
                            super().accept()
                    
                    
                    JonBJ 1 Reply Last reply
                    0
                    • R RoXus

                      This code probably works fine for saving dialogs, but (at least in my X11-environment) opening of a file does not, as the dialog closes as soon as a file is selected. But I found a solution by subclassing QFileDialog. The most important snippets of my Python-code are as follows:

                          def __init__(self, parent=None):
                              ...
                              # set custom dialog option
                              self.setOption(QFileDialog.DontUseNativeDialog)
                              # add your own widgets to the dialog
                              layg: QGridLayout = self.layout()
                              # the standard layout of QFileDialog is a QGridLayout with 4 rows and 3 columns; 
                              # so add your widgets from row 5 onwards
                              ...
                              # redirect accept signal from open button, which is a QDialogButtonBox in 
                              # row 2, column 2
                              itm_open = layg.itemAtPosition(2, 2)
                              itm_open.widget().accepted.connect(self.accept_button)      
                              self.show()
                      
                          def accept(self):
                              # override the standard accept function, which is triggered
                              # on file selection
                              logger.debug('accepted via file selection')
                              filename = self.selectedFiles()[0]
                              # I e.g. used the additional widgets to display parts of the contents 
                              # of the file; this code follows here
                              ...
                              # and IMPORTANT: do NOT call super().accept(), as this closes 
                              # the dialog immediately
                      
                          def accept_button(self):
                              # react on the click of the open button
                              logger.debug('accepted via button')
                              ...
                              # now it is time to call super().accept()
                              super().accept()
                      
                      
                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #11

                      @RoXus said in Subclassing QFileDialog:

                          itm_open = layg.itemAtPosition(2, 2)
                          itm_open.widget().accepted.connect(self.accept_button)      
                      

                      Just an observation. For you or anyone else reading this, obviously this relies on the dialog having a QGridLayout and the desired button being positioned in cell (2, 2). Probably a more robust approach is to find the QDialogButtonBox, or better the actual button. From Python you can start from

                      box = self.findChild(QDialogButtonBox)
                      # or
                      buttons = self.findChildren(QPushButton)
                      

                      Look at the docs for these, you can e.g. add the objectName() if you know what it is for the button you want, or similar.

                      1 Reply Last reply
                      4

                      • Login

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