Ограничить количество файлов, которые пользователь может выбрать в QFileDialog

Вот код:

dialog = new QFileDialog(this);
dialog->setFileMode(QFileDialog::ExistingFiles);
connect(dialog, SIGNAL(currentChanged(const QString&)),
    this, SLOT(dialogSelectionChanged(const QString&)));

void MainWindow::dialogSelectionChanged(const QString& file)
{
    QStringList selected = dialog->selectedFiles();
}

Проблема в том, что

  • fileSelected(const QString&) и filesSelected(const QStringList&) выдаются только после того, как я нажму кнопку "Открыть".
  • currentChanged(const QString&) пропускает только вновь выбранный файл
  • и selectedFiles() возвращает мне в этом случае файлы, которые были выбраны на предыдущем шаге. Вероятно, диалог обновляет файлы после того, как выдает currentChanged(const QString&).

Вот почему я не знаю, как отслеживать выбранные файлы. Другая проблема заключается в том, что я пока не знаю, как отменить выбор последнего выбранного файла, если пользователь превышает лимит разрешенных вариантов.

Я видел несколько примеров, когда люди реализовывали пользовательские функции либо с помощью QProxyModel, либо с пользовательской реализацией QFileDialog, но я не уверен, что лучше всего подойдет для моих нужд.


person JuicyKitty    schedule 17.06.2020    source источник
comment
Вы пытались переопределить selectFile(const QString& filename)? Возможно, это тот, кого вы ищете.   -  person rbaleksandar    schedule 17.06.2020
comment
Из документации не похоже, что это виртуальная функция   -  person drescherjm    schedule 17.06.2020
comment
Это не так, я проверил источники. Кроме того, этот метод не используется в реализации, это просто часть пользовательского API, не более   -  person JuicyKitty    schedule 17.06.2020


Ответы (2)


Это можно сделать, если вы готовы принять бессовестный взлом :-)

Предполагая следующий код...

QFileDialog fd;

Проверка fd с использованием fd.findChildren<QListView *>() показывает, что у него есть два дочерних элемента, которые либо являются наследниками QListView...

  1. QListView с именем listView
  2. QSidebar названная боковая панель

(где QSidebar является частным для Qt).

Работая в предположении, что QListView с именем listView представляет собой интересующий вас виджет, вы можете подключить обратный вызов к его модели выбора...

QFileDialog fd;
for (const auto &i: fd.findChildren<QListView *>("listView")) {
  auto *sm = i->selectionModel();
  QObject::connect(sm, &QItemSelectionModel::selectionChanged,
                   [sm](const QItemSelection &selected, const QItemSelection &deselected)
                     {

                       /*
                        * Here we pass a hard-coded max selected items
                        * value of 5 to the real callback/slot.
                        */
                       handle_selection_updated(5, sm, selected, deselected);
                     });
}

Здесь handle_selection_updated имеет следующее определение...

void handle_selection_updated (int selection_max, QItemSelectionModel *sm,
                               const QItemSelection &selected,
                               const QItemSelection &deselected)
{

  /*
   * We need to remember the last valid selection.  The following
   * is declared static in this simple case but would generally be
   * a class member in `real' code.
   */
  static QItemSelection last_selected;

  /*
   * Because we update the selection model `sm' from within this
   * slot this function will recurse which will cause problems if
   * we don't detect it and take appropriate action.
   */
  static bool recursing = false;
  if (recursing)
    return;

  /*
   * If the number of rows selected is greater than the value
   * specified by `selection_max' then revert to the last valid
   * selection as specified by `last_selected'.
   */
  if (sm->selectedRows().size() > selection_max) {

    /*
     * The following call to QItemSelectionModel::clearSelection
     * will result in a recursive call to this function.  Set
     * `recursing' to true to catch this and avoid associated
     * problems.
     */
    recursing = true;

    /*
     * Now clear the selection and reset it to the items from
     * `last_selected'.
     */
    sm->clearSelection();
    for (const auto &i: last_selected.indexes()) {
      sm->select(i, QItemSelectionModel::Select);
    }
    recursing = false;
  }

  /*
   * Update `last_selected'.
   */
  last_selected = sm->selection();
}

Я только кратко протестировал приведенный выше код, но, похоже, он ведет себя так, как требуется.

person G.M.    schedule 18.06.2020

для этого нет метода, но как только ваш клиент сделает выбор, вы можете проверить операцию, вызвав метод выбранные файлы

размер этого QStringList больше вашего ограничения, вы можете прервать операцию и показать сообщение об ошибке.

нравится

void MainWindow::dialogSelectionChanged(const QString& file)
{
    QStringList selected = dialog->selectedFiles();
    if(selected.size()>LIMIT)
    {
        showErrorMsg("some helpful mesage");
    }

}
person ΦXocę 웃 Пepeúpa ツ    schedule 17.06.2020
comment
Это то, что я собираюсь использовать, если не найду лучшего решения. Спасибо! - person JuicyKitty; 17.06.2020
comment
другого нет, если только вы не напишете свой собственный Диалог ¯\_(ツ)_/¯ - person ΦXocę 웃 Пepeúpa ツ; 17.06.2020