Russian Qt Forum
Сентябрь 30, 2024, 22:36 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проводник файлов  (Прочитано 8923 раз)
IUnknown
Гость
« : Апрель 17, 2007, 20:00 »

В программе нужно сделать проводник для файлов, что бы иметь возможность их перетаскивать в проэкт только для Windows (как в Nero). Основные требования - быстрый и красивый. QDirModel не подходит - очень убогий. Делаю свой класс QFileListModel : public QAbstractTableModel. Информация о каждом файле имеет вид
Код:

class FileInfo
{
public:
FileInfo()
{}

FileInfo(const FileInfo& ci)
{
//
m_bSysIcon = ci.m_bSysIcon;
m_icon = ci.m_icon;
m_pathName = ci.m_pathName;
m_bIsUpDir = ci.m_bIsUpDir;
m_FileInfo = ci.m_FileInfo;
//
}

const FileInfo& operator=(const FileInfo& stringSrc)
{
//
m_bSysIcon = stringSrc.m_bSysIcon;
m_icon = stringSrc.m_icon;
m_pathName = stringSrc.m_pathName;
m_bIsUpDir = stringSrc.m_bIsUpDir;
m_FileInfo = stringSrc.m_FileInfo;
//
return *this;
}

virtual ~FileInfo()
{}
//
QString m_pathName;
bool m_bSysIcon;
QIcon m_icon;
bool m_bIsUpDir;
QFileInfo m_FileInfo;
//
};

Получаю список файлов и сохраняю его QList<FileInfo*> m_FileList. В QVariant QFileListModel::data(const QModelIndex &index, int role) const отображаю информацию о файле. В функции которая получает список файлов запускаю поток, который должен подгружать системные иконки. Все вродебы нормально, пока не попробовать перейти в другую директорию, пока в текущей еще не закончилась подгрузка иконок. Вот класс потока:
Код:

QIconsLoaderThread::QIconsLoaderThread(QObject *parent)
: QThread(parent)
{
m_bAbort = false;
}

QIconsLoaderThread::~QIconsLoaderThread()
{
QMutexLocker locker(&m_mutex);
    m_bAbort = true;
}

void QIconsLoaderThread::Load()
{
qDebug()<<"Call Load()";
    QMutexLocker locker(&m_mutex);
    if (!isRunning()) {
m_bAbort = false;
        start();
    }
}

void QIconsLoaderThread::run()
{
qDebug()<<"Call run()";
QFileListModel* pModel = qobject_cast<QFileListModel*>(this->parent());
QMutexLocker locker(&m_mutex);
for(int i=0; i<=pModel->m_FileList.count()-1; i++)
{
if (m_bAbort)
{
pModel = 0;
qDebug()<<"Thread stoped!";
break;
}
FileInfo* pFileInfo = const_cast<FileInfo*>(pModel->m_FileList[i]);
if((!pFileInfo->m_bSysIcon) && (!pFileInfo->m_FileInfo.isDir()))
{
pFileInfo->m_icon = GetIcon(pFileInfo->m_pathName);
pFileInfo->m_bSysIcon = true;
}
pFileInfo = 0;
emit pModel->reset();
}
}

void QIconsLoaderThread::stop()
{
qDebug()<<"Call stop()";
    QMutexLocker locker(&m_mutex);
if(isRunning())
{
m_bAbort = true;
wait();
}
}

Я догадываюсь что они m_FileList не могут поделить, но что не пробую не могу никак синхронизовать. Буду благодарен за помощь.  Улыбающийся

добавлено спустя 2 минуты:

 А да в начале функции которая заполняет список файлами вызываю    m_pLoaderThread->stop(), а в конце m_pLoaderThread->Load().
Записан
SABROG
Гость
« Ответ #1 : Апрель 17, 2007, 21:11 »

Можно создать QList с типом QPair, где первый элемент - указатель на модель, второй указатель на нить.
Создадим новую модель и установим в нужный item view.
Перед созданием новой нити пробегаемся по QList'у выхватывая указатель на нити, делаем проверку на ее активность, если нить уже не работает - берем из первого параметра указатель на модель и удаляем указатели на нить и модель, затем удаляем элемент из QList'a.
Создаем новую нить и передаем эту модель ей, добавляем указатели в QList.

Плюс - быстрое переключение между директориями.
Минус - чем быстрее навигация по директориям тем больше кушается память и процессорное время на нитях.
Записан
IUnknown
Гость
« Ответ #2 : Апрель 17, 2007, 22:16 »

А может можно както мютексами все сделать?
Записан
SABROG
Гость
« Ответ #3 : Апрель 18, 2007, 07:26 »

Посмотри в сторону QWaitCondition и QSemaphore. Я так понимаю, там метод wait есть, который будет выполняться так долго, пока ресурс не освободит другая нить.
Только вот может быть ситуация, что одна нить подгрузит и расставит одни иконки, а другая дождавшись пока ресурс освободиться - расставит уже другие, в некоторых случаях может произойти перезапись, что нам и нужно, а если в предыдущей директории файлов было больше, то вылезет хвост в виде дополнительных иконок на файлы, которых нет в этой директории.
Записан
Gryz
Гость
« Ответ #4 : Апрель 18, 2007, 16:04 »

m_bAbort - статический член?

 и m_mutex тоже?

 или какие они?

 пока что, как я понимаю, модель одна для всех директорий?
Записан
IUnknown
Гость
« Ответ #5 : Апрель 18, 2007, 20:09 »

Не статические, нужно иметь несколько файловых списков (дерево, список). И они должны работать автономно ( не так как в проводнике виндовс). А нить не подгрузит другие иконки. Мне нужно при двойном клике на директорию войти в нее, тоесть старое содержимое m_FileList очищается и заполняется файлами из новой директории. Тоесть мне нужно при клике на директорию завершить процесс подгрузки иконок для текущей(если он сам не завершился - не для всех файлов были вытянуты иконки) и начать новый для новой директории. Хотелось бы при переходе в другую директорию остановить процесс для текущей, дождатся пока он перестанет использовать m_FileList, очистить m_FileList и запустить процесс для директорию в которую я вхожу.
Записан
SABROG
Гость
« Ответ #6 : Апрель 18, 2007, 21:01 »

Значит перед входом в новую директорию взываешь QThread::terminate() или QThread:exit()/QThread::quit() , потом вызываешь QThread::wait(), чтобы дождаться, пока она *корректно* завершиться.

В первом случае нить прервется в любой точке выполнения, если нет данных которые при этом могут потеряться или записаться неверно или крашнуть прогу, то это лучший выбор, надо не забыть поставить QThread::setTerminationEnabled ( bool enabled = true ). Если надо дождаться выполнения определенных действий, то quit или exit.
Записан
Gryz
Гость
« Ответ #7 : Апрель 19, 2007, 13:08 »

Т.е. я понимаю, схема такая.
Для навигации в одном View используется один экземпляр класса QFileListModel. Этот экземпляр, т.е. модель, владеет одним экземпляром класса QIconsLoaderThread. После одновления списка файлов, т.е. списка FileInfo, модель запускает поток для чтения иконок.

При переходе в другую директорию возможен, вариант, что поток не успеет доработать цикл до конца. Необходимо его прервать.

Тогда
QThread:exit()/QThread::quit()  использовать не получится. Т.к. не запущен event loop потока. Возможно только прерывание  QThread::terminate() . Или установкой m_bAbort в true, как сделано у тебя.

Непонятно, зачем в десрукторе это:  m_bAbort = true;

добавлено спустя 43 минуты:

 
Код:
void QIconsLoaderThread::Load() 
{
   qDebug()<<"Call Load()";
    QMutexLocker locker(&m_mutex);
    if (!isRunning()) {
      m_bAbort = false;
        start();
    }
}


Вот здесь locker не сможен захватить мутекс, пока поток этого объекта выполняется (в методе run захват мутекс происходит практически сразу). Так что проверка !isRunning() необходима только для

Код:
  qDebug()<<"Call run()"; 
   QFileListModel* pModel = qobject_cast<QFileListModel*>(this->parent());


эти две строки в принципе тоже можно выполнять после захвата мутекса.

В методе stop аналогичная ситуация.

чтение и установку m_bAbort лучше реализовать через методы и защитить отдельным мутексом.

добавлено спустя 27 минут:

 также советую в методе run()  использовать не список FileInfo* не тот, что в модели, а создавать его копию.

не вижу никакой защиты от одновременного использования полей FileInfo запущенным потоком QIconsLoaderThread и gui-потоком
Записан
IUnknown
Гость
« Ответ #8 : Апрель 19, 2007, 20:53 »

Не очень понял как защитить одновременное использования полей FileInfo запущенным потоком QIconsLoaderThread и gui-потоком? Но есть прогресс - после защиты m_bAbort мютексами я наконец увидел qDebug()<<"Thread stoped!". Но ошибка все-равно вылетает.
Записан
IUnknown
Гость
« Ответ #9 : Апрель 22, 2007, 18:46 »

QIconsLoaderThread::QIconsLoaderThread(QObject *parent)
   : QThread(parent)
{
   SetStop(false);
}

QIconsLoaderThread::~QIconsLoaderThread()
{
}

void QIconsLoaderThread::Load()
{
   qDebug()<<"Call Load()";
    if (!isRunning()) {
      SetStop(false);
        start();
    }
}

void QIconsLoaderThread::run()
{
   qDebug()<<"Call run()";
   QMutexLocker locker(&m_mutex1);
   QFileListModel* pModel = qobject_cast<QFileListModel*>(this->parent());
   for(int i=0; i<=pModel->m_FileList.count()-1; i++)
   {
      if (IsStoped())
      {
         pModel = 0;
         qDebug()<<"Thread stoped!";
         return;
      }
      FileInfo* pFileInfo = const_cast<FileInfo*>(pModel->m_FileList);
      
      if((!pFileInfo->m_bSysIcon) && (!pFileInfo->m_FileInfo.isDir()))
      {
         pFileInfo->m_icon = GetIcon(pFileInfo->m_pathName);
         pFileInfo->m_bSysIcon = true;
      }
      pFileInfo = 0;
      emit pModel->reset();
   }
}

void QIconsLoaderThread::stop()
{
   qDebug()<<"Call stop()";

      SetStop(true);
}

QPixmap QIconsLoaderThread::convertHIconToPixmap( const HICON icon) const
{
}

QIcon QIconsLoaderThread::GetIcon(const QString & fileName) const
{}

void QIconsLoaderThread::SetStop(bool bStop)
{
   m_mutex.lock();
   m_bAbort = bStop;
   m_mutex.unlock();
}
bool QIconsLoaderThread::IsStoped() const
{
   m_mutex.lock();
   bool tmp = m_bAbort;
   m_mutex.unlock();
   return tmp;
}
Так переделал класс потока.
В классе модели все обращения к списку с файлами m_FileList обставил мютексами. Все-равно возникает та же ошибка.
Записан
Gryz
Гость
« Ответ #10 : Апрель 23, 2007, 09:47 »

Думаю, класс FileInfo стоит переделать так:
Код:

class FileInfo
{
private:
    class IconLocker
    {
    public:
        IconLocker(QIcon * icon, QMutex * lock):m_icon(icon), m_lock(lock)
        {
            m_lock->lock();
        }
        ~IconLocker()
        {
            m_lock->unlock();
        }
        QIcon * operator->()
        {
            return m_icon;
        }
    private:
        QIcon * m_icon;
        QMutex * m_lock;
    };
public:
    FileInfo()
    {}

    FileInfo(const FileInfo& ci)
    {
        //
        m_bSysIcon = ci.m_bSysIcon;
        m_icon = ci.m_icon;
        m_pathName = ci.m_pathName;
        m_bIsUpDir = ci.m_bIsUpDir;
        m_FileInfo = ci.m_FileInfo;
        //
    }

    const FileInfo& operator=(const FileInfo& stringSrc)
    {
        //
        m_bSysIcon = stringSrc.m_bSysIcon;
        m_icon = stringSrc.m_icon;
        m_pathName = stringSrc.m_pathName;
        m_bIsUpDir = stringSrc.m_bIsUpDir;
        m_FileInfo = stringSrc.m_FileInfo;
        //
        return *this;
    }

    virtual ~FileInfo()
    {}
    IconLocker getIcon()
    {
        return IconLocker(&m_icon, &m_memberLock);
    }

    void setIcon(const QIcon & icn)
    {
        QMutexLocker locker(&m_memberLock);
        m_icon = icn;
    }
    bool isSysIcon()
    {
        QMutexLocker locker(&m_memberLock);
        return m_bSysIcon;
    }
    bool isDir()
    {
        QMutexLocker locker(&m_memberLock);
        return m_FileInfo.isDir();
    };
    //

    QString m_pathName;
    bool m_bSysIcon;
    QIcon m_icon;
    bool m_bIsUpDir;
    QFileInfo m_FileInfo;
    QMutex m_memberLock;
    //
};


В методе run делать копию списка из модели:

QList<FileInfo*> locList = pModel->getFileList()

причем, в модели оставить защиту мутексом только в getFileList().

Сообщение о сбросе модели посылать не напрямую, а через postEvent или через вызов ее спец слота при соединении с сигналом потока с типом соединения Qt::QueuedConnection. И вообще, что-то я не вижу в базовом классе модели сигнала reset();
Записан
IUnknown
Гость
« Ответ #11 : Апрель 24, 2007, 16:16 »

Переделал класс FileInfo. В классе модели создал:
Код:

QFileListModel::QFileListModel(QObject *parent)
: QAbstractTableModel(parent)
{
connect(this, SIGNAL(reset()),this, SLOT(reset()));
m_pLoaderThread = new QIconsLoaderThread(this);
//m_iCount = GetLocalFiles(QString("c:\\"));
SetCurrentPath(QString("c:\\"));
}

QList<FileInfo*> QFileListModel::GetFileList() const
{
QMutexLocker locker(&m_mutex);
return m_FileList;
}

void QFileListModel::reset()
{
QMutexLocker locker(&m_mutex);
QAbstractTableModel::reset();
}

В функции потока run вызываю:
Код:

QList<FileInfo*> locList = pModel->GetFileList();
и
QMetaObject::invokeMethod(pModel, "reset", Qt::QueuedConnection);

Все-равно вылетает та же ошибка. Если не использовать reset - она не возникает, но мне как-то же нужно перерисовать список.  :shock:
Записан
Gryz
Гость
« Ответ #12 : Апрель 24, 2007, 17:50 »

Код:
connect(this, SIGNAL(reset()),this, SLOT(reset())); 

не пойму, как это вообще работает  :? .

а если так попробовать (это слот):

Код:
void QFileListModel::notifyAboutChanges(int fileNumber) 
{
    emit dataChanged(index(fileNumber, m_colNumber), index(fileNumber, m_colNumber));    
}

где fileNumber - номер строки с описанием файла в модели (предполагаю, что совпадает с номером в списке); m_colNumber - номер столбца, где рисуется иконка.

а в методе run:

 
Код:
QMetaObject::invokeMethod(pModel, "notifyAboutChanges", Qt::QueuedConnection,
                           Q_ARG(int, i));


и что за ошибка вылетает?
Записан
IUnknown
Гость
« Ответ #13 : Апрель 24, 2007, 23:14 »

Спасибо большое! Все работает замечательно.
Записан
Gryz
Гость
« Ответ #14 : Апрель 25, 2007, 09:23 »

Пожалуйста Улыбающийся

Смотрю только, доступ к списку файлов в модели все-таки остается несинхронизированным. Это можно исправить используя "опимизированный мутекс" QReadWriteLock в классе модели.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.129 секунд. Запросов: 21.