Wiki

Основанный на QListBox файловый браузер

Jacek Surazski (перевод Racheengel)

Эта статья представляет FileBrowser, подкласс QListBox, позволяющий пользователю просматривать каталоги и файлы.В отличие от FileDialog, который работает как модальное окно, элемент FileBrowser может быть встроен в главное окно приложения или в диалог, предоставляя больше удобств для просмотра.

Компонент FileBrowser отображает содержимое каталога. Если пользователь дважды кликнет имя подкаталога, FileBrowser загрузит в себя содержимое этого подкаталога (как показано ниже). По двойному щелчку на "..", пользователь может вернуться в родительский каталог.

Filebrowser1 -> Filebrowser2

Когда пользователь подсвечивает новый файл, FileBrowser активирует сигнал, к которому может быть подключена другая чать приложения. Например, на рисунке ниже представлено приложения, комбинирующее FileBrowser с QTextBrowser для получения простого браузера HTML.

Textbrowser

Самый обычный путь реализовать FileBrowser - наследоваться от QListBox и добавить несколько новых членов:

class FileBrowser : public QListBox
{
    Q_OBJECT
public:
    FileBrowser(const QString &filter, QWidget *parent = 0, const char *name = 0);
    void setDir(const QString &path);
 
signals:
    void picked(const QString &fileName);
 
private slots:
    void itemHighlighted(int index);
    void itemSelected(int index);
 
private:
    QString nameFilter;
    QString basePath;
};
 

(Альтернативой может быть наследование от QWidget и использование потомка QListBox, но это требует несколько больше кода.)

Конструктор FileBrowser'а получает строку с фильтром имен в дополнение к обычным параметрам QWidget parent и name. Эта строка должна содержать список шаблонов имен файлов (например, "*.jpg *.gif *.png"), и FileBrowser будет отображать только файлы, подпадающие под один из шаблонов.

Функция setDir() устанавливает начальный каталог для просмотра. Сигнал picked(const QString &) активируется, когда новая запись становится подсвеченной (кроме каталогов). В типичных приложениях, этот сигнал должен быть присоеденен к слоту, отображающему содержимое файла.

Далее идут два "закрытых" слота. Они присоединяются к сигналам QListBoxhighlighted(int) и selected(int). Сигнал highlighted(int) активируется, только когда пользователь дважды щелкнул запись или нажал Enter. Для элемента FileBrowser, мы должны использовать оба сигнала - одиночный щелчок на файле вызовет его просмотр, но двойной щелчок (или нажатие Enter) необходимо для входа в каталог.

Наконец, FileBrowser имеет два "закрытых" члена данных: nameFilter хранит аргумент конструктора filter (фильтр), а basePath содержит путь к каталогу, который отображен в данное время в списке.

FileBrowser::FileBrowser(const QString &filter, QWidget *parent, const char *name)
    : QListBox(parent, name)
{
    nameFilter = filter;
    setDir(QDir::currentDirPath());
    connect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
    connect(this, SIGNAL(selected(int)), this, SLOT(itemSelected(int)));
}

Конструктор сохраняет параметр nameFilter для дальнейшего использования. Затем он вызывает setDir() для заполнения себя содержимым текущего рабочего каталога и присоединяет два сигнала, наследованных от QListBox, к соответствующим "закрытым" слотам FileBrowser.

void FileBrowser::setDir(const QString &path)
{
    QDir dir(path, nameFilter, QDir::DirsFirst);
    dir.setMatchAllDirs(true);
    if (!dir.isReadable())
        return;
    clear();
 
    QStringList entries = dir.entryList();
    QStringList::ConstIterator it = entries.constBegin();
    while (it != entries.constEnd()) {
        if (*it != ".")
            insertItem(*it);
        ++it;
    }
    basePath = dir.canonicalPath();
}
 

Функция setDir() использует объект QDir для получения списка содержимого каталога, указанного в path. Мы передаем nameFilter в конструктор QDir, так что он вернет только файлы, имя которых совпадает с шаблоном. Мы также передаем флаг DirsFirst для указания того, чтобы QDir вначале возвращал каталоги. Потом мы вызываем setMatchAllDirs(true), чтобы QDir вывел все подкаталоги, а не только те, что совпадают с фильтром.

Перед чтением каталога, надо убедится, что он существует и доступен для чтения. Если каталог нечитаем, setDir() сразу выполнит возврат, оставляя список неизмененным.

Далее мы очищаем QListBox и заполняем его содержимым каталога, которое вернул QDir::entryList()--- но пропускаем каталог ".". Мы сохраняем текущий путь в "закрытом" члене basePath для дальнейшего использования.

void FileBrowser::itemHighlighted(int index)
{
    QString path = basePath + "/" + text(index);
    if (QFileInfo(path).isFile())
        emit picked(path);
}
 

Whenever a list box entry is highlighted (single-clicked), the itemHighlighted() slot is called with the entry's index. We pass it to QListBox::text() to retrieve the file name. We use a QFileInfo object to find out whether the selected entry is a regular file. If it is, we emit the picked() signal.

void FileBrowser::itemSelected(int index)
{
    QString path = basePath + "/" + text(index);
    if (QFileInfo(path).isDir())
        setDir(path);
}
 

Когда запись в списке выбрана (дважды кликнута), слот itemSelected() вызывается со значением индекса записи. Если выбранная запись является каталогом, FileBrowser обновляет себя содержимым этого каталога.

Напишем мальнькую программу, которая позволяет это использовать:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QSplitter win(Qt::Horizontal);
 
    QString filter = "*.htm *.html *.txt *.xml";
    FileBrowser *fileBrowser = new FileBrowser(filter, &win);
    QTextBrowser *textBrowser = new QTextBrowser(&win);
    QObject::connect(fileBrowser, SIGNAL(picked(const QString &)),
                     textBrowser, SLOT(setSource(const QString &)));
 
    app.setMainWidget(&win);
    win.show();
    return app.exec();
}
 

Эта программа создает FileBrowser с левой стороны и QTextBrowser с правой, с разделителем между ними. Мы инициализируем FileBrowser фильтром имен, который совпадает с форматами, поддерживаемыми QTextBrowser.

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


Copyright © 2004 Trolltech Trademarks