Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Native file dialog on Linux Ubuntu "swallows" file extensions



  • In the past, when I call the static QFileDialog::getOpenFileName() and QFileDialog::getSaveFileName() functions, the window manager in Ubuntu appears to hide the file extension from the user, although it filters the files correctly. I.e. if I pass a filter like CSV files (*.csv), on Ubuntu Nautilus the drop-down list at the bottom will only show "CSV files". So to get the "(*.csv)" part to show, I have been setting the filters like this: "CSV files [*.csv] (*.csv)" and the user will see "CSV files [*.csv]" which is OK.

    Now I discovered that I am getting a lot of Gtk warnings about GtkDialog mapped without a transient parent and have read somewhere that if you pass the option QFileDialog::DontUseNativeDialog, you can make the warnings go away. However, when I do that, the filter shows both the [*.csv] AND the (*.csv) part, which is obviously very strange for the user to see.

    Also, I would rather use the native dialog if possible because the user then has access to the bookmarks, etc. which are not shown in the non-native dialog.

    It is very strange because all other applications seem to get the file extension to show up in the selection list with no problem ... only in Qt. Is this a known bug?



  • I think this is a bug. Have a look at this function in the file qgtk3dialoghelpers.cpp (beginning at line 473 using Qt 5.11.1 sources):

    void QGtk3FileDialogHelper::setNameFilters(const QStringList &filters)
    {
        GtkDialog *gtkDialog = d->gtkDialog();
        foreach (GtkFileFilter *filter, _filters)
            gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);
    
        _filters.clear();
        _filterNames.clear();
    
        foreach (const QString &filter, filters) {
            GtkFileFilter *gtkFilter = gtk_file_filter_new();
            const QString name = filter.left(filter.indexOf(QLatin1Char('(')));
            const QStringList extensions = cleanFilterList(filter);
    
            gtk_file_filter_set_name(gtkFilter, qUtf8Printable(name.isEmpty() ? extensions.join(QLatin1String(", ")) : name));
            foreach (const QString &ext, extensions)
                gtk_file_filter_add_pattern(gtkFilter, qUtf8Printable(ext));
    
            gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
    
            _filters.insert(filter, gtkFilter);
            _filterNames.insert(gtkFilter, filter);
        }
    }
    

    According to the Gtk docs, the function gtk_file_filter_set_name() is supposed to set the "human-readable" part of the file filter. But the code immediately above it has stripped the variable name of the extension, so it seems obvious that it won't be shown.

    I seem to remember that on Windows, one could add the extension twice in round parentheses; however, it will get cut off at the first round left parenthesis here in this function.

    Should I file a bug report? Or has this been fixed in later versions of Qt?


  • Lifetime Qt Champion

    @robert-hairgrove said in Native file dialog on Linux Ubuntu "swallows" file extensions:

    Or has this been fixed in later versions of Qt?

    I don't know, but you can test with the recent Qt version.



  • Test case using the filter string from the Qt docs. Please note that the behavior probably is only shown on Linux with Gtk window manager.

    #include <QMainWindow>
    #include <QApplication>
    #include <QPushButton>
    #include <QtCore>
    #include <QFileDialog>
    #include <QMessageBox>
    
    int main(int argc, char *argv[])
    {
      QApplication a(argc, argv);
      QMainWindow w;
      QPushButton b;
      b.setText("Bye!");
      w.setCentralWidget(&b);
      w.connect(&b, &QPushButton::clicked, &w, &QMainWindow::close);
    
      QString fn = QFileDialog::getOpenFileName(
            &w,
            "Testing the native dialog",
            QString(),
            "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)");
      if (!fn.isEmpty()) {
        QString msg = "You chose the file:\n";
        QMessageBox::information(&w,"File Selected",msg.append(fn));
      }
      fn = QFileDialog::getOpenFileName(
             &w,
             "Testing the Qt (non-native) dialog",
            QString(),
            "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)",
            nullptr, QFileDialog::DontUseNativeDialog);
      if (!fn.isEmpty()) {
        QString msg = "You chose the file:\n";
        QMessageBox::information(&w,"File Selected",msg.append(fn));
      }
    
      w.show();
      return a.exec();
    }
    


  • I just filed a bug report here.



  • I'm trying to create a workaround until this is fixed. Is there any way to determine which platform theme is used at compile time, or lacking that at runtime? I can use #ifdef __linux__ etc., but there are certainly other window managers which might be used and not just Gtk. And I couldn't find any Gtk-specific macros to use.

    And since the platform dialog helpers (QPA) are plugins, maybe a runtime decision as to how to format the filter string would be most appropriate. How can I do this?


  • Lifetime Qt Champion

    Hi,

    You can use QSysInfo for runtime checks.

    You have the Q_OS_XXX macros for build time separation.



  • @SGaist Thanks ... these are great suggestions, but I couldn't find any way of querying QSysInfo for the window manager currently being used. I would need something Gnome or Gtk-specific, I suppose.


  • Lifetime Qt Champion

    This thread might give you some clues:



  • @SGaist Fantastic ... this will certainly help ... thank you!



  • @Robert-Hairgrove In the meantime, I have resorted to iterating over the environment variables which are available in the main() function if you declare it like this:

    int main(int argc, char*argv[], char*envp[]) { /*...*/ }
    

    If any of the strings contain "GNOME" or "GTK_", I can set a global flag which allows me to mogrify the filter string so that it isn't swallowed. So if my filter string looks like this:

    "Text files (*.txt)"
    

    then I insert two Unicode look-alike characters (u8'FF08' and u8'FF09', full-width parentheses) and duplicate the extension, i.e.:

    "Text files(*.txt) (*.txt)"
    

    The user will see: "Text files(*.txt)" which is good enough for my purposes.


Log in to reply