Russian Qt Forum
Июнь 28, 2017, 05:05 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Жуткая проблема: не могу правильно обработать выбор строки в QListView  (Прочитано 5594 раз)
xintrea
Хакер
*****
Offline Offline

Сообщений: 676



Просмотр профиля WWW
« : Июль 03, 2011, 23:50 »

Здравствуйте!


Уже третий день бъюсь над простейшей задачей.

Есть  меня QListView. Нужно сделать слот, который бы срабатывал при каждом переключении курсора на другую строку. Ну, то есть, кликнул человек мышкой на нужную строку - выполнился слот. Нажал человек клавишу вверх/вниз, выбралась другая строка, - нужно вызвать тот же слот.

Задача проста. А правильного решения сделать не могу, и вот почему.

Клик мышки по строке списка можно отловить с помощью сигнала clicked() объекта QListView. Он работает правильно, без нареканий.

Но нам еще нужно отлавливать движение клавишами по списку. А это движение можно отловить только с помощью сигнала select-модели списка, именуемого currentRowChanged(). Прототип этого сигнала:

Код:
currentRowChanged (const QModelIndex & current, const QModelIndex & previous)

Получается, что слот-обработчик нужно подключать к двум сигналам - к clicked() и currentRowChanged(). Но тогда при выборе нужной записи мышкой, слот срабатывает два (!) раза - один раз от клика, и второй раз из-за того, что засветка переместилась на другую строку. А мне нужно чтоб слот вызывался один раз! Ведь клик-то был один.


Но и это еще не всё. Сигнал currentRowChanged() для QListView, в котором не выбрана ни одна запись, вырабатывается очень хитрым способом. У этого сигнала два параметра - QModelIndeх выбранной записи и QModelIndeх предыдущей записи. Мы для простоты получаем из этих индексов номера записей методом row(). Закономерность генерируемых сигналов и их параметров следующая:

- Если кликнуть по записи с номером 0 (верхняя запись), то сгенерируется один сигнал с параметрами: (0, -1).

- Если кликнуть по записи с номером, отличным от нуля то сгенерируется два (!) сигнала с параметрами: (0, -1) и (номер_записи, 0).

- Если выбрать нужную запись программно (через selection model), то сгенерируется один сигнал с параметрами: (номер_записи, -1).


Из-за такого дикого поведения, слот-обработчик может быть вызван три (!) раза (один раз сигналом clicked(), два раза сигналом currentRowChanged(), если ткнуть на любую запись кроме первой). А мне нужно, чтоб слот был вызван один раз.


Я решил отказаться от сигнала clicked(), так как currentRowChanged() в моей задаче "перекрывает" его функциональность. Но так как этот currentRowChanged() ведет себя дико - генерируется два раза при выборе не первой строки в списке, - я не могу его толком использовать.


Вопрос: как мне отследить выбор строки в списке QListView так, чтобы слот срабатывал только один раз? Нужно учесть, что выбирать пользователь может как кликом мышкой, так и движением засветки клавишами клавиатуры.
« Последнее редактирование: Июль 04, 2011, 00:13 от xintrea » Записан

Собираю информацию по крупицам
http://webhamster.ru
Kolobok
Бывалый
*****
Offline Offline

Сообщений: 499


Просмотр профиля
« Ответ #1 : Июль 04, 2011, 11:15 »

Используй QItemSelectionModel::currentChanged ( const QModelIndex & current, const QModelIndex & previous )

Но описанное поведение ненормально и у меня не воспроизводится.
« Последнее редактирование: Июль 04, 2011, 11:18 от Kolobok » Записан
xintrea
Хакер
*****
Offline Offline

Сообщений: 676



Просмотр профиля WWW
« Ответ #2 : Июль 04, 2011, 14:47 »

Используй QItemSelectionModel::currentChanged ( const QModelIndex & current, const QModelIndex & previous )

Но описанное поведение ненормально и у меня не воспроизводится.

Попробовал currentChanged() вместо currentRowChanged() - результат тот же.

Странно, что у тебя не воспроизводится. Я просто соединяю один слот с одним сигналом currentChanged():

Код:
// Обработка изменения засветки в списке конечных записей
connect(recordView->selectionModel(),
            SIGNAL(currentChanged (const QModelIndex&, const QModelIndex&)),
            this,
            SLOT(onSelectionRecordChanged(const QModelIndex&, const QModelIndex&)));

В слоте пишу:

Код:
// Действия при изменении местоположения указателя в списке конечных записей
void RecordTableScreen::onSelectionRecordChanged(const QModelIndex &indexCurrent,
                                                 const QModelIndex &indexPrevious)
{
 qDebug() << "RecordTableScreen::onSelectionRecordChanged()";
 qDebug() << "Current index row() " << indexCurrent.row() << " isValid() " << indexCurrent.isValid();
 qDebug() << "Previous index row() " << indexPrevious.row() << " isValid() " << indexPrevious.isValid();
}

При клике по пятой записи в логе вижу:

Код:
[DBG] RecordTableScreen::onSelectionRecordChanged()
[DBG] Current index row()  0  isValid()  true
[DBG] Previous index row()  -1  isValid()  false
[DBG] RecordTableScreen::onSelectionRecordChanged()
[DBG] Current index row()  4  isValid()  true
[DBG] Previous index row()  0  isValid()  true

То есть, вначале засветка "скрыто" устанавливается на первую запись (индекс 0), и только потом перескакивает на пятую (индекс 4).

У тебя наверно глюк не повторяется потому, что на форме только один список QListView и он сразу активный (то есть, выбор записи с индексом 0 происходит сразу при первичной отрисовке QListView). А ты сделай еще кнопочку рядом, и сделай ее активной по-умолчанию. То есть, чтобы засветки (курсора) на ни на одной строке списка не стояло. Тогда при клике мышой на какой-нибудь строке списка, ты наверно увидишь генерацию двух сигналов. Если этого не будет наблюдаться значит баг локальный.

У меня Qt 4.6.3, Linux.
Записан

Собираю информацию по крупицам
http://webhamster.ru
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3904



Просмотр профиля WWW
« Ответ #3 : Июль 04, 2011, 16:22 »

я тоже не могу воспроизвести твою ошибку про 2 сигнала сразу. поставил кнопку активной по дефолту:
Код
C++ (Qt)
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
   ui.setupUi(this);
 
fsm = new QFileSystemModel;
fsm->setRootPath(".");
ism = new QItemSelectionModel(fsm, ui.listView);
ui.listView->setModel(fsm);
ui.listView->setSelectionModel(ism);
ui.listView->setRootIndex(fsm->index("."));
 
connect(ism, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(rowChanged(QModelIndex,QModelIndex)));
}
 
void MainWindow::rowChanged(const QModelIndex &newIndex, const QModelIndex &oldIndex)
{
qDebug() << "old" << oldIndex.data() << "new" << newIndex.data();
}
Цитата: лог
old QVariant(, ) new QVariant(QString, "debug")
old QVariant(QString, "debug") new QVariant(QString, "Makefile.Debug")
old QVariant(QString, "Makefile.Debug") new QVariant(QString, "vc90.pdb")
old QVariant(QString, "vc90.pdb") new QVariant(QString, "release")
old QVariant(QString, "release") new QVariant(QString, "debug")

Есть ещё момент - когда закрываешь окно приложения другим окном/сворачиваешь, а потом снова делаешь окно активным, то отправляется сингал (1 раз) и выбирается первая строка (невидимое выделение).

Прикрепил проект на всякий случай. У меня 4.7.3, Win7 x86.
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
xintrea
Хакер
*****
Offline Offline

Сообщений: 676



Просмотр профиля WWW
« Ответ #4 : Июль 04, 2011, 16:57 »

я тоже не могу воспроизвести твою ошибку про 2 сигнала сразу. поставил кнопку активной по дефолту

Я собрал твой проект. Почему-то при старте активен список, а не кнопка. Я даже попробовал добавить

Код:
pushButton->setFocus();

после ui.setupUi(this); - всеравно активен список. Почему так - пока не понял.


Но дело в другом. Вот твой лог:

Цитата: лог
old QVariant(, ) new QVariant(QString, "debug")

- вот это оно и есть. То есть, пользователь не совершал никаких действий - по списку не щелкал, клавишами засветку не перемещал. А сигнал сгенерировался.


Есть ещё момент - когда закрываешь окно приложения другим окном/сворачиваешь, а потом снова делаешь окно активным, то отправляется сингал (1 раз) и выбирается первая строка (невидимое выделение).

У меня такого в твоем проекте не наблюдается. Но всеравно, если это под виндой так, то тоже неправильно - ведь пользователь над списком никаких действий не делает. А сигнал генерируется.


Вот и встает вопрос: как сделать так, чтобы реация шла только на действия пользователя?
Записан

Собираю информацию по крупицам
http://webhamster.ru
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3904



Просмотр профиля WWW
« Ответ #5 : Июль 04, 2011, 17:10 »

Но дело в другом. Вот твой лог:

Цитата: лог
old QVariant(, ) new QVariant(QString, "debug")

- вот это оно и есть. То есть, пользователь не совершал никаких действий - по списку не щелкал, клавишами засветку не перемещал. А сигнал сгенерировался.
нет, это я как раз кликнул на debug - сам по себе сигнал не генерировался.

собрал под маком (4.7.2) - поведение абсолютно такое же, как и на винде. правда случайным образом фокус с кнопки слетал при запуске и тогда сигнал отправлялся сам по себе, но pushButton->setFocus(); решил эту проблему. и кстати при скрытии окна и потом после показа сигнал не отправляется.

так что есть подозрение, что виновата 4.6 версия...
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
xintrea
Хакер
*****
Offline Offline

Сообщений: 676



Просмотр профиля WWW
« Ответ #6 : Июль 04, 2011, 17:22 »

old QVariant(, ) new QVariant(QString, "debug")
нет, это я как раз кликнул на debug - сам по себе сигнал не генерировался.

У тебя debug - в первой строке. А ты запусти программу, и сразу ткни _не_на_первую_ строчку.

Я ж об этом и писал, что двойной сигнал генерится на строках, отличных от первой.
Записан

Собираю информацию по крупицам
http://webhamster.ru
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3904



Просмотр профиля WWW
« Ответ #7 : Июль 04, 2011, 17:26 »

я это тоже проверял и два сигнала не отправляется (не просто ж так я написал что не могу воспроизвести баг):
Цитата: лог
old QVariant(, ) new QVariant(QString, "translation_test")
old QVariant(QString, "translation_test") new QVariant(QString, "Library")
old QVariant(QString, "Library") new QVariant(QString, "Users")
old QVariant(QString, "Users") new QVariant(QString, "Руководства пользователя и информация")
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
xintrea
Хакер
*****
Offline Offline

Сообщений: 676



Просмотр профиля WWW
« Ответ #8 : Июль 04, 2011, 17:30 »

я это тоже проверял и два сигнала не отправляется (не просто ж так я написал что не могу воспроизвести баг):
Цитата: лог
old QVariant(, ) new QVariant(QString, "translation_test")
old QVariant(QString, "translation_test") new QVariant(QString, "Library")
old QVariant(QString, "Library") new QVariant(QString, "Users")
old QVariant(QString, "Users") new QVariant(QString, "Руководства пользователя и информация")

Угу, теперь вижу. Походу значит это я на что-то подобное нарвался:

http://bugreports.qt.nokia.com/browse/QTBUG-8086

Исправили в 4.7.
Записан

Собираю информацию по крупицам
http://webhamster.ru
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  

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