Russian Qt Forum

Qt => Model-View (MV) => Тема начата: xintrea от Июль 03, 2011, 23:50



Название: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: xintrea от Июль 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 так, чтобы слот срабатывал только один раз? Нужно учесть, что выбирать пользователь может как кликом мышкой, так и движением засветки клавишами клавиатуры.


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: Kolobok от Июль 04, 2011, 11:15
Используй QItemSelectionModel::currentChanged ( const QModelIndex & current, const QModelIndex & previous )

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


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: xintrea от Июль 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.


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: kambala от Июль 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.


Название: Re: Жуткая проблема: не могу правильно обрабо
Отправлено: xintrea от Июль 04, 2011, 16:57
я тоже не могу воспроизвести твою ошибку про 2 сигнала сразу. поставил кнопку активной по дефолту

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

Код:
pushButton->setFocus();

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


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

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

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


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

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


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


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: kambala от Июль 04, 2011, 17:10
Но дело в другом. Вот твой лог:

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

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

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

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


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: xintrea от Июль 04, 2011, 17:22
old QVariant(, ) new QVariant(QString, "debug")
нет, это я как раз кликнул на debug - сам по себе сигнал не генерировался.

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

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


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: kambala от Июль 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, "Руководства пользователя и информация")


Название: Re: Жуткая проблема: не могу правильно обработать выбор строки в QListView
Отправлено: xintrea от Июль 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 (http://bugreports.qt.nokia.com/browse/QTBUG-8086)

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