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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: QTreeView + QFileSystemModel - редактируемое имя папки в качестве имени виджета  (Прочитано 6142 раз)
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« : Апрель 10, 2015, 06:50 »

Установить редактируемое имя папки/файла в качестве имени заголовка.
Думал не заводить новый топик. Однако, все таки это разные задачи, соответственно разные темы и проще найти ответ при решении подобных задач.
Казалось бы простая задачка. Однако бился день прежде чем обратиться к профи. Надеюсь, что для профи она действительно не составит труда. Этот код работает и для файлов, но что бы не путать, ограничился пояснением с каталогами.
Код
C++ (Qt)
   connect( treeView, SIGNAL( clicked( QModelIndex ) ),
            this, SLOT( setNameTree( QModelIndex ) ) );
 
   connect( cFileSystemModel, SIGNAL( fileRenamed( QString, QString, QString ) ),
            this, SLOT( changeCurrentIndex() ) );
}
...
...
 cFileSystemModel->mkdir(index, newNameFolder);
 index = cFileSystemModel->index(newFullNameFolder);
 treeView->setCurrentIndex(index);
 
 treeView->edit(index);
 setWindowTitle(newNameFolder);
}
 
void MainWindow::setNameTree(const QModelIndex &index)
{
 setWindowTitle( cFileSystemModel->fileInfo(index).baseName() );
}
 
void MainWindow::changeCurrentIndex()
{
 setNameTree( treeView->currentIndex() );
}

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

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

Есть один неприятный момент:
Если пользователь меняет имя каталога (дважды кликнув) или создает новый каталог и меняет его имя, но при этом кликает на другой каталог, как и положенно имя этого каталога идет в заголовок. Но при этом в заголовке окна мелькает имя которое ввел пользователь, а затем уже устанавливается текущее.

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

Соответсвенно вопрос. Как избавиться от этого мелькания - от промежуточной ненужной смены имени окна?

Тестовый код с воспроизведенной проблемой прилагается.
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #1 : Апрель 11, 2015, 08:04 »

Понимаю, задача кажется простенькой, что и браться за нее не стоит. Так же думал и оставил на последок. Однако с этой стороны подойти не удается. Сигналы идут в порядке: сначала закончено редактирование, затем слик мыши. И с этим поделать ничего не могу.
Ладно пытаемся подойти с другой стороны и переопределяем события:
Код
C++ (Qt)
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
 if(event->key() == Qt::Key_Return)
 {
   setNameTree( treeView->currentIndex() );
 }
}
 
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
 if(event->button() == Qt::LeftButton)
 {
   setNameTree( treeView->currentIndex() );
 }
}

Enter работает как надо. К сожалению чего не скажешь о клитке мыши. Клик работает если нажать на виджет. Что и понятно в нем это событие и переопредлено. При клике в дереве, событие уже не срабатывает, хотя оно и находится в виджете. Можно ли как то простым способом это подправить?
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #2 : Апрель 12, 2015, 08:56 »

Все таки пришлось создать класс унаследованный от QTreeView (может и к лучшему) и для него уже переопределить событие mouseReleaseEvent. Вроде как работает все. Хотя вдруг начинает тормозить переставая переключать ветки дерева. Видимо еще осознать надо как делать это правильно.

Однако пытаюсь разобраться с фильтрами событий и решить задачку через них. Тут тоже пришлось определить класс фильтра и вроде как установил для treeView. Однако не срабатывает. Понимаю, что что то недопонимаю Улыбающийся. Может кто опытным взглядом оценит и скажет, что не так?
Код
C++ (Qt)
#include <QDebug>
 
#include "MainWindow.h"
#include "ui_MainWindow.h"
 
CFileSystemView::CFileSystemView(QWidget *parent)
 : QTreeView(parent)
{}
 
CFileSystemView::~CFileSystemView()
{}
 
MouseFilter::MouseFilter(QObject *object)
 : QObject(object)
{}
 
 
MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),
 ui(new Ui::MainWindow)
{
 ui->setupUi(this);
 
 treeView = new CFileSystemView;
 cFileSystemModel = new CFileSystemModel;
 treeMouseFilter = new MouseFilter(treeView);
 
 treeView->installEventFilter(treeMouseFilter);
 
 treeView->setModel(cFileSystemModel);
 setCentralWidget(treeView);
 
 treeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
 
 treeView->setDragEnabled(true);
 treeView->setAcceptDrops(true);
 treeView->setDropIndicatorShown(true);
 
 treeView->setDragDropMode( QAbstractItemView::InternalMove );
 
 treeView->setContextMenuPolicy( Qt::CustomContextMenu );
 
 connect( treeView, SIGNAL( customContextMenuRequested( const QPoint & ) ),
          this, SLOT( showCustomMenu( const QPoint & ) ) );
}
 
MainWindow::~MainWindow()
{
 delete ui;
}
 
void MainWindow::createNewFolder()
{
 QModelIndex index = treeView->currentIndex();
 QString rootForNewFolder;
 
 if(!index.isValid())
 {
   index = treeView->rootIndex();
   rootForNewFolder = cFileSystemModel->fileInfo(index).absoluteDir().absolutePath();
 }
 else if(cFileSystemModel->fileInfo(index).isFile())
 {
   rootForNewFolder = cFileSystemModel->fileInfo(index).absoluteDir().absolutePath();
 }
 else
 {
   rootForNewFolder = cFileSystemModel->filePath(index);
 }
 
 index = cFileSystemModel->index(rootForNewFolder);
 QString newNameFolder = "FOLDER";
 QString newFullNameFolder = cFileSystemModel->filePath(index) + "/FOLDER";
 int nName = 0;
 
 while( QDir( newFullNameFolder ).exists() )
 {
   newNameFolder = "FOLDER" + QString::number(++nName);
   newFullNameFolder = cFileSystemModel->filePath(index) +  "/" + newNameFolder;
 }
 
 cFileSystemModel->mkdir(index, newNameFolder);
 index = cFileSystemModel->index(newFullNameFolder);
 treeView->setCurrentIndex(index);
 
 treeView->edit(index);
 setWindowTitle(newNameFolder);
}
 
void MainWindow::setNameTree()
{
 setWindowTitle( cFileSystemModel->fileInfo(treeView->currentIndex()).baseName() );
}
 
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
 if(event->key() == Qt::Key_Return)
  setNameTree();
}
 
void MainWindow::showCustomMenu(QPoint const & pos)
{
 m_managerMenu = new QMenu( treeView );
 m_managerMenu->addAction( tr( "New Folder" ), this, SLOT(createNewFolder()) );
 m_managerMenu->exec( treeView->mapToGlobal( pos ) );
}
 
 
 
bool MouseFilter::eventFilter(QObject* object, QEvent* event)
{
 if( event->type() == QEvent::MouseButtonPress )
 {
   QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
   if(mouseEvent->button() == Qt::LeftButton)
   {
     qDebug() << "FILTER";
     return true;
   }
   else
     return false;  
 }
 return false;
}
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #3 : Апрель 12, 2015, 17:12 »

Работающий код на основе переопределения события клика мыши.
Отрабатывается отпускание левой кнопки после клика. Если отрабатывать момент нажатия, код не работает. Видимо переопределять функцию в этом случае надо полностью.
Код
C++ (Qt)
#include <QDebug>
 
#include "MainWindow.h"
#include "ui_MainWindow.h"
 
CFileSystemView::CFileSystemView(QWidget *parent)
 : QTreeView(parent)
{}
 
CFileSystemView::~CFileSystemView()
{}
 
 
MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),
 ui(new Ui::MainWindow)
{
 ui->setupUi(this);
 
 treeView = new CFileSystemView(this);
 cFileSystemModel = new CFileSystemModel(this);
 
 treeView->setModel(cFileSystemModel);
 setCentralWidget(treeView);
 
 treeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
 
 treeView->setDragEnabled(true);
 treeView->setAcceptDrops(true);
 treeView->setDropIndicatorShown(true);
 
 treeView->setDragDropMode( QAbstractItemView::InternalMove );
 
 treeView->setContextMenuPolicy( Qt::CustomContextMenu );
 
 connect( treeView, SIGNAL( customContextMenuRequested( const QPoint & ) ),
          this, SLOT( showCustomMenu( const QPoint & ) ) );
 
 connect( treeView, SIGNAL( clicked( QModelIndex ) ),
          this, SLOT( setNameTree() ) );;
 
 connect( treeView, SIGNAL( reName() ),
          this, SLOT( setNameTree() ) );
}
 
MainWindow::~MainWindow()
{
 delete ui;
}
 
 
void MainWindow::createNewFolder()
{
 QModelIndex index = treeView->currentIndex();
 QString rootForNewFolder;
 
 if(!index.isValid())
 {
   index = treeView->rootIndex();
   rootForNewFolder = cFileSystemModel->fileInfo(index).absoluteDir().absolutePath();
 }
 else if(cFileSystemModel->fileInfo(index).isFile())
 {
   rootForNewFolder = cFileSystemModel->fileInfo(index).absoluteDir().absolutePath();
 }
 else
 {
   rootForNewFolder = cFileSystemModel->filePath(index);
 }
 
 index = cFileSystemModel->index(rootForNewFolder);
 QString newNameFolder = "FOLDER";
 QString newFullNameFolder = cFileSystemModel->filePath(index) + "/FOLDER";
 int nName = 0;
 
 while( QDir( newFullNameFolder ).exists() )
 {
   newNameFolder = "FOLDER" + QString::number(++nName);
   newFullNameFolder = cFileSystemModel->filePath(index) +  "/" + newNameFolder;
 }
 
 cFileSystemModel->mkdir(index, newNameFolder);
 index = cFileSystemModel->index(newFullNameFolder);
 treeView->setCurrentIndex(index);
 
 treeView->edit(index);
 setWindowTitle(newNameFolder);
}
 
void MainWindow::setNameTree()
{
 setWindowTitle( cFileSystemModel->fileInfo(treeView->currentIndex()).baseName() );
}
 
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
 if(event->key() == Qt::Key_Return)
 {
   qDebug() << "NAME" << cFileSystemModel->fileInfo(treeView->currentIndex()).baseName();
   setNameTree();
 }
}
 
void MainWindow::showCustomMenu(QPoint const & pos)
{
 m_managerMenu = new QMenu( treeView );
 m_managerMenu->addAction( tr( "New Folder" ), this, SLOT(createNewFolder()) );
 m_managerMenu->exec( treeView->mapToGlobal( pos ) );
}
 
 
 
void CFileSystemView::mouseReleaseEvent(QMouseEvent *event)
{
 if(event->button() == Qt::LeftButton)
 {
   qDebug() << "NAME" << currentIndex();
   emit reName();
 }
 else
   QTreeView::mouseReleaseEvent(event);
}
Если быстро нажимать по пунктам файлового дерева, то в какой то момент курсор останавливается на одном месте.
Может в этот раз кто нибудь даст совет, что подправить?
Событие клавиши ENTER отлавливается в виджете окна, може стоит ее так же ловить в самом дереве?
Во вложении рабочий тестовый вариант.
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #4 : Апрель 13, 2015, 04:08 »

Утро вечера мудреннее:
Код
C++ (Qt)
void CFileSystemView::mousePressEvent(QMouseEvent *event)
{
 QTreeView::mousePressEvent(event);
 emit reName();
}
Пока во всех отношениях устраивает, во всяком случае в тестовом коде.
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #5 : Апрель 14, 2015, 04:23 »

Переписал код в оригинальную программу, где менеджер встроен в окошке.
После создания нового файла имя файла уходит в заголовок. После этого происходит пересортировка дерева. И все работает как положенно, но только если пользователь рабоатет в пределах окошка дерва.
Но если пользователь ввел новое имя файла, но при этом кликнул мышкой в пределах приложения, но за окном виджета дерева то в этом случае имя окна меняется, но дальше уже не срабатывает клик мыши и пересортировки не происходит.
Могу принудительно вызвать слот сортировки после окончания редактирования, но тут свои проблемы, связанные с тем что если пользователь ввел имя и нажал иной пункт дерва, то после сортировки курсор встает не на нажатый пункт, а на соседний, если введенное имя оказалось выше. Как то не очень интуитивно, хотя и каприз.

Хочу попробовать эмуляию нажатия клавиши ENTER (вместо сортировки). Вопрос как это делается?
Т.е. как вообще эмулируется нажатие клавиши в пределах приложения или окошка?
« Последнее редактирование: Апрель 14, 2015, 04:31 от Serega » Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #6 : Апрель 15, 2015, 03:27 »

В конечном итоге в случае если пользователь при смене имени словаря ткнул курсор мыши на другой словарь, курсор автоматом ставится на место переименнованного словаря после пересортировки. Это осуществить оказалось намного проще, да и пожалуй более логично и полностью рабочую/сортируемую модель сделать не составило труда.
Записан
Serega
Самовар
**
Offline Offline

Сообщений: 127


Просмотр профиля
« Ответ #7 : Апрель 16, 2015, 15:33 »

Вычищаю код. Немного запутался.
Ситуация такова:
Создается виджет дерева. Курсор ставится автоматом на определенную позицию, т.е. файл. Информация из этого файла должна поступить в другой виджет. Для этого фактически из конструктора виджета дерева необходимо выслать сигнал, что как я понял нельзя.
Как быть? Может подскажет кто?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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