2014-12-02 4 views
0

Я пытаюсь написать диалоговое окно, которое позволяет пользователю выбирать среди любого смонтированного USB-накопителя, найденного в системе. В Windows я могу определенно получить эту информацию вручную, используя вызовы как GetLogicalDriveStrings, так и GetDriveType, поэтому я мог бы создать простой список таким образом. Но пользователь также должен иметь возможность перейти на любой из USB-накопителей, чтобы выбрать нужную папку для записи файла. Я взглянул на QFileSystemModel, но я не вижу, как ограничить (фильтровать) его только показом установленных USB-накопителей и их дочерних папок/файлов. Кто-нибудь имеет представление о том, как это лучше всего сделать с помощью Qt-рамки?QTreeView и QFileSystemModel для отображения только USB-накопителей


Обновлено - 12/3/24:

docsteer, спасибо за предложение. Похоже, это правильный путь. Я внедрил предложенное изменение, и у меня случился сбой, который случается чаще всего, когда я запускаю приложение. Он показывает C :, и ждет некоторое время, затем сбой с кодом ошибки 255. Я предполагаю, что есть что-то, что я не правильно подключил здесь, но пока не смог понять это. В те времена, когда он не разбился, я все еще вижу полный список доступных дисков в системе, включая два USB-устройства, которые я подключил, вместо того, чтобы просто видеть USB-диски. Если я изменю строку 42 в filesystemmodeldialog.cpp, так что я передаю «dir» вместо «usbModel», нет сбоя. Можете ли вы или кто-нибудь увидеть что-нибудь здесь, что может привести к сбою, и любая причина, по которой созданный мной USBDriveFilterProxyModel, который правильно выбирает два USB-устройства со всех подключенных дисков, не работает для фильтрации данных в представлении? Я дал все файлы из моего небольшого тестового приложения, включая заголовок, сгенерированный из файла .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(); 
} 

mainwindow.cpp:

#ifndef MAINWINDOW_H 
#define MAINWINDOW_H 

#include <QMainWindow> 

class FileSystemModelDialog; 
class QFileSystemModel; 

namespace Ui { 
class MainWindow; 
} 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

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

private: 
    void detectUsb(); 

private: 
    Ui::MainWindow *ui; 
    FileSystemModelDialog *treeView; 
    QFileSystemModel *fileSystemModel; 
}; 

#endif // MAINWINDOW_H 

mainwindow.cpp:

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

#include <QFileSystemModel> 

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

    treeView = new FileSystemModelDialog(this); 
    setCentralWidget(treeView); 
    fileSystemModel = new QFileSystemModel; 
} 

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

filesystemmodeldialog.h:

#ifndef FILESYSTEMMODELDIALOG_H 
#define FILESYSTEMMODELDIALOG_H 

#include "ui_filesystemmodelwidget.h" 

#include <QWidget> 
#include <QFileSystemModel> 
#include <QItemDelegate> 

class USBDriveFilterProxyModel; 

class IconItemDelegate : public QItemDelegate 
{ 
public: 
    IconItemDelegate(); 
    QSize sizeHint (const QStyleOptionViewItem & option, const QModelIndex & index) const; 
}; 

class IconFileSystemModel : public QFileSystemModel 
{ 
    Q_OBJECT 
public: 
    virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; 

}; 

class FileSystemModelDialog : public QWidget 
{ 
    Q_OBJECT 

public: 
    explicit FileSystemModelDialog(QWidget *parent); 
    ~FileSystemModelDialog(); 

private: 
    Ui::FileSystemModelWidget *ui; 
    IconFileSystemModel *dir; 
    USBDriveFilterProxyModel *usbModel; 

Q_SIGNALS: 
    void signalFileSelected(QString); 
}; 

#endif // FILESYSTEMMODELDIALOG_H 

filesystemmodeldialog.cpp:

#include "filesystemmodeldialog.h" 
#include "usbdrivefilter.h" 

IconItemDelegate::IconItemDelegate(){} 

QSize IconItemDelegate::sizeHint (const QStyleOptionViewItem & /*option*/, const QModelIndex & index) const 
{ 
    const QFileSystemModel *model = reinterpret_cast<const QFileSystemModel *>(index.model()); 
    QFileInfo info = model->fileInfo(index); 
    if(info.isDir()) 
    { 
     return QSize(40,40); 
    } 
    else 
    { 
     return QSize(64,64); 
    } 
} 

QVariant IconFileSystemModel::data (const QModelIndex & index, int role) const 
{ 
    // will do more, but for now, just paints to view 
    return QFileSystemModel::data(index, role); 
} 

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

    dir = new IconFileSystemModel(); 
    dir->setRootPath("\\"); 
    dir->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); 

    usbModel = new USBDriveFilterProxyModel(this); 
    usbModel->setSourceModel(dir); 
    usbModel->setDynamicSortFilter(true); 

    IconItemDelegate *iconItemDelegate = new IconItemDelegate(); 

    ui->treeView->setModel(usbModel); 
    // hide unneeded hierachy info columns 
    ui->treeView->hideColumn(1); 
    ui->treeView->hideColumn(2); 
    ui->treeView->hideColumn(3); 
    ui->treeView->setItemDelegate(iconItemDelegate); 
} 

FileSystemModelDialog::~FileSystemModelDialog() 
{ 
    delete dir; 
} 

usbdrivefilter.h:

#ifndef USBDRIVEFILTER_H 
#define USBDRIVEFILTER_H 

#include <QSortFilterProxyModel> 
#include <QModelIndex> 
#include <QString> 

class USBDriveFilterProxyModel : public QSortFilterProxyModel 
{ 
    Q_OBJECT 
public: 
    explicit USBDriveFilterProxyModel(QObject *parent = 0); 

protected: 
    virtual bool filterAcceptsRow(
     int source_row, const QModelIndex &source_parent) const; 

private: 
    void getMountedRemovables(); 

private: 
    QList<QString> removables; 

}; 

#endif // USBDRIVEFILTER_H 

usbdrivefilter.cpp:

#include "usbdrivefilter.h" 

#include <windows.h> 

USBDriveFilterProxyModel::USBDriveFilterProxyModel(QObject *parent) : 
    QSortFilterProxyModel(parent) 
{ 
    getMountedRemovables(); 
    // will eventually also register for changes to mounted removables 
    // but need to get passed my current issue of not displaying only USBs. 
} 

bool USBDriveFilterProxyModel::filterAcceptsRow(int sourceRow, 
    const QModelIndex &sourceParent) const 
{ 
    QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent); 

    // Since drive string can have more than just "<DriveLetter>:", need 
    // to check each entry in the usb list for whether it is contained in 
    // the current drive string. 

    for (int i = 0; i < removables.size(); i++) 
    { 
     if (sourceModel()->data(index0).toString().contains(removables[i])) 
     { 
      return true; 
     } 
    } 
    return false; 
} 

void USBDriveFilterProxyModel::getMountedRemovables() 
{ 
    DWORD test = GetLogicalDrives(); 

    DWORD mask = 1; 
    UINT type = 0; 
    WCHAR wdrive[] = L"C:\\"; // use as a drive letter template 
    for (int i = 0; i < 32; i++) 
    { 
     if (test & mask) 
     { 
      wdrive[0] = (char)('A' + i); // change letter in template 
      type = GetDriveType(wdrive); 
      switch (type) { 
      case DRIVE_REMOVABLE: 
      { 
       QString qdrive = QString((char)('A' + i)) + ":"; 
       removables.append(qdrive); 
       break; 
      } 
      default: break; 
      } 
     } 
     mask = mask << 1; 
    } 
} 

ui_filesystemmodelwidget.h:

#ifndef UI_FILESYSTEMMODELWIDGET_H 
#define UI_FILESYSTEMMODELWIDGET_H 

#include <QtWidgets/QApplication> 
#include <QtWidgets/QHeaderView> 
#include <QtWidgets/QTreeView> 

QT_BEGIN_NAMESPACE 

class Ui_FileSystemModelWidget 
{ 
public: 
    QTreeView *treeView; 

    void setupUi(QWidget *FileSystemModelWidget) 
    { 
     if (FileSystemModelWidget->objectName().isEmpty()) 
      FileSystemModelWidget->setObjectName(QStringLiteral("FileSystemModelWidget")); 
     FileSystemModelWidget->resize(670, 755); 
     treeView = new QTreeView(FileSystemModelWidget); 
     treeView->setGeometry(QRect(0, 0, 670, 531)); 
     treeView->setObjectName(QStringLiteral("treeView")); 
     treeView->setAutoFillBackground(true); 
     treeView->setStyleSheet(QLatin1String(" QScrollBar:vertical {\n" 
"  width: 61px;\n" 
" background-color: rgb(227, 227, 227);\n" 
" }\n" 
" QScrollBar::handle:vertical {\n" 
"  min-height: 50px;\n" 
" }\n" 
"\n" 
"QTreeView, QListView {\n" 
" alternate-background-color: rgb(226, 226, 226);\n" 
" font-size: 16px;\n" 
"  show-decoration-selected: 1;\n" 
" }\n" 
"\n" 
" QTreeView::item, QListView::item {\n" 
" height: 22px;\n" 
"  border: 1px solid transparent;\n" 
"  border-top-color: transparent;\n" 
"  border-bottom-color: transparent;\n" 
" }\n" 
"\n" 
" QTreeView::item:selected, QListView::item::selected {\n" 
"  border: 1px solid #567dbc;\n" 
" background-color: rgb(85, 170, 255);\n" 
" }\n" 
"\n" 
"\n" 
" QTreeView::branch:has-siblings:!adjoins-item {\n" 
"  border-image: url(:/new/prefix1/images/vline.png) 0;\n" 
" }\n" 
"\n" 
" QTreeView::branch:has-siblings:adjoins-item {\n" 
"  border-image: url(:/new/prefix1/images/branch-more.png) 0;\n" 
" }\n" 
"\n" 
" QTreeView::branch:!has-children:!has-siblings:adjoins-item {\n" 
"  border-image: url" 
         "(:/new/prefix1/images/branch-end.png) 0;\n" 
" }\n" 
"\n" 
" QTreeView::branch:has-children:!has-siblings:closed,\n" 
" QTreeView::branch:closed:has-children:has-siblings {\n" 
"   border-image: none;\n" 
"   image: url(:/new/prefix1/images/branch-closed.png);\n" 
" }\n" 
"\n" 
" QTreeView::branch:open:has-children:!has-siblings,\n" 
" QTreeView::branch:open:has-children:has-siblings {\n" 
"   border-image: none;\n" 
"   image: url(:/new/prefix1/images/branch-open.png);\n" 
" }\n" 
"")); 
     treeView->setFrameShape(QFrame::Box); 
     treeView->setFrameShadow(QFrame::Plain); 
     treeView->setHorizontalScrollMode(QAbstractItemView::ScrollPerItem); 
     treeView->setExpandsOnDoubleClick(true); 
     treeView->header()->setVisible(false); 
     treeView->header()->setStretchLastSection(true); 

     retranslateUi(FileSystemModelWidget); 

     QMetaObject::connectSlotsByName(FileSystemModelWidget); 
    } // setupUi 

    void retranslateUi(QWidget *FileSystemModelWidget) 
    { 
     FileSystemModelWidget->setWindowTitle(QApplication::translate("FileSystemModelWidget", "Form", 0)); 
    } // retranslateUi 

}; 

namespace Ui { 
    class FileSystemModelWidget: public Ui_FileSystemModelWidget {}; 
} // namespace Ui 

QT_END_NAMESPACE 

#endif // UI_FILESYSTEMMODELWIDGET_H 

Обновлено - 12/4/24:

Так я обнаружил, что авария происходит в IconItemDelegate :: sizeHint() в файле filesystemusbmodeldialog.cpp. Пробег доходит до линии 9:

QFileInfo info = model->fileInfo(index); 

и переходить в эту точку дает нарушение доступа.Я предполагаю, что это потому, что я заменил объект IconFileSystemUsbModel на USBDriveFilterProxyModel как модель QTreeView в конструкторе FileSystemUsbModelDialog. Поэтому я предполагаю, что листинг index.model() в IconItemDelegate :: sizeHint() является некорректным, и теперь мне нужно получить исходную исходную модель перед вызовом fileInfo(). Поэтому я изменил перегрузку sizeHint() на следующее:

QSize IconItemUsbDelegate::sizeHint (const QStyleOptionViewItem & /*option*/, const QModelIndex & index) const 
{ 
    const USBDriveFilterProxyModel *model = reinterpret_cast<const USBDriveFilterProxyModel *>(index.model()); 
    const QFileSystemModel *fsmodel = reinterpret_cast<const QFileSystemModel *>(model->sourceModel()); 
    QFileInfo info = fsmodel->fileInfo(index); 
    if(info.isDir()) 
    { 
     return QSize(40,40); 
    } 
    else 
    { 
     return QSize(64,64); 
    } 
} 

Но это не помогло. Тогда я нашел ссылку, которая, казалось, сказать, что мне нужно позвонить setRootIndex() на мой взгляд, в настоящее время, что прокси-сервер вместо модели, так что я добавил, что в моем конструктор FileSystemUsbModelDialog, который теперь выглядит следующим образом:

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

    dir = new IconFileSystemUsbModel(); 
    dir->setRootPath("\\"); 
    dir->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); 

    usbModel = new USBDriveFilterProxyModel(this); 
    usbModel->setSourceModel(dir); 
    usbModel->setDynamicSortFilter(false); 

    IconItemUsbDelegate *iconItemDelegate = new IconItemUsbDelegate(); 

    ui->treeView->setModel(usbModel); 
    ui->treeView->setRootIndex(usbModel->mapFromSource(dir->setRootPath("\\"))); 
    // hide unneeded hierachy info columns 
    ui->treeView->hideColumn(1); 
    ui->treeView->hideColumn(2); 
    ui->treeView->hideColumn(3); 
    ui->treeView->setItemDelegate(iconItemDelegate); 
} 

Это не сработало. Я вернулся к своему IconItemUsbDelegate :: sizeHint() и изменил его, подумав, что, возможно, установить root в представлении было все, что мне действительно нужно было делать, и не повезло.

Любые мысли?

ответ

0

Так docsteer меня на правильном пути , как я заявил в своих обновлениях, но, как я уже сказал, я получил сбой при входе в метод sizeHint() моего делегата item , За чужое предложение, я ставлю некоторые отладочные в выяснить, что показал индекс следующим образом:

qDebug() << index.isValid(); 
qDebug() << "text = " << index.data(); 
qDebug() << "Row = " << index.row() << "Column = " << index.column(); 

и я обнаружил, что содержание индекса конкретно к тому, что я бы ожидать, что модель прокси содержит, а не модель файловой системы. Более внимательно, я понял, что передал индекс, связанный с прокси-моделью, в метод fileInfo() для приведения в модель файловой системы. Я изменил его, как показано ниже, чтобы я сначала применил модель индекса к указателю типа модели прокси, затем я получу указатель модели источника (файловой системы) и вызову его метод fileInfo(). Но теперь я сначала отобразить индекс к индексу источника, и я передать результат этого отображения в метод FILEINFO() модели системы файла, который в настоящее время работает как шарм:

const USBDriveFilterProxyModel *model = reinterpret_cast<const USBDriveFilterProxyModel *>(index.model()); 
const QFileSystemModel *fsmodel = reinterpret_cast<const QFileSystemModel *>(model->sourceModel()); 
QFileInfo info = fsmodel->fileInfo(index); 
if(info.isDir()) 
{ 
    return QSize(40,40); 
} 
else 
{ 
    return QSize(64,64); 
} 

Спасибо за помощь.

1

Я предлагаю сделать это с помощью QSortFilterProxyModel. Примером может выглядеть

.header

class USBDriveFilter : public QSortFilterProxyModel 
{ 
    Q_OBJECT; 
public: 
    USBDriveFilter(QObject *parent = 0); 
protected: 
    // Reimplemented from QSortFilterProxyModel 
    virtual bool filterAcceptsRow (int source_row, const QModelIndex & source_parent) const; 
}; 

.cpp

bool USBDriveFilter::filterAcceptsRow(int sourceRow, 
     const QModelIndex &sourceParent) const 
{ 
    QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent); 
    // This is a naive example and just doesn't accept the drive if the name 
    // of the root node contains C: - you should extend it to check the letter 
    // against your known list of USB drives derived from the windows API 
    return (!sourceModel()->data(index0).toString().contains("C:")); 
} 

Чтобы использовать эту функцию, вы могли бы сделать что-то вроде

QFileSystemModel *m = new QFileSystemModel(this); 
USBDriveFilter *filter = new USBDriveFilter(this); 
filter->setSourceModel(m); 
// Now use filter as your model to pass into your tree view. 

 Смежные вопросы

  • Нет связанных вопросов^_^