Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: l_a_m от Апрель 15, 2010, 12:25



Название: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 15, 2010, 12:25
Доброе время суток Ув. Коллеги!
Ситуация такая: есть QListWidget, требуется отловить изменение выбранного объекта, и при определённых условиях вернуть выделение сделанное пользователем.
пример:
есть список Item-ов:
1
2
3
4

пользователь выбирает Item1, потом выбирает Item3, появляется сообщение - мол "вы уверены, что хотите выбрать именно этот Item?", если да - то выбираем Item3, если нет, то возвращаем выделение Item1. Версия Qt 4.6.2 ОС: Windows (но возможно приложение будет затачиваться под кроссплатформенность).

делаю я примерно следующее:
Код:
QListWidget myList;
connect(&myList, SIGNAL(currentItemChanged (QListWidgetItem *, QListWidgetItem *)), this, SLOT(MySelectedChanged(QListWidgetItem *, QListWidgetItem *)));

//---
MySelectedChanged(QListWidgetItem * current, QListWidgetItem * previous)
{
   myList.setCurrentItem(previous); //до здравствует рекурсия!!!!
}

собственно вопрос, можно ли как либо заблокировать сигнал currentItemChanged? да бы избежать бесконечной рекурсии?
зы: пробовал делать:
Код:
MySelectedChanged(QListWidgetItem * current, QListWidgetItem * previous)
{
   myList.blockSignals(true);
   myList.setCurrentItem(previous); //до здравствует рекурсия!!!!
   myList.blockSignals(false);
}
тогда вообще ничего не происходит :-(. подскажите как быть. Заранее благодарен.



Название: Re: QListWidget и война с currentItemChanged
Отправлено: GreatSnake от Апрель 15, 2010, 12:36
На самом деле, делать такие вещи в слотах не рекомендуется - у Qt может легко поехать крыша.
Зарядите QTimer и уже в нём изменяйте текущий элемент не забыв про рекурсию.


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 15, 2010, 12:42
благодарю за ответ, если можно примерчик, т.к. я в Qt пока новичок. И если можно, объясните почему не рекомендуется делать так в слоте?


Название: Re: QListWidget и война с currentItemChanged
Отправлено: alexman от Апрель 15, 2010, 12:57
Попробуй использовать методы:
void QListWidget::itemSelectionChanged ()   [signal]
QList<QListWidgetItem *> QListWidget::selectedItems () const
void QListWidgetItem::setSelected ( bool select )
...


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 15, 2010, 13:01
благодарю за ответ, пробовал... Но! так я знаю, что произошло событие которое привело к изменению выделения Item-a, знаю какой текущий Item или коллекция Item-ов выделина, но не знаю какой предыдущий Item был выделен. как быть? :-) вижу только добавление отдельного (внешнего) Item-a, который будет хранить в себе предыдущее значение. но как по мне - это не очень красиво, разве нет более симпотишного способа? :-)
и ещё: можно ли как либо заблокировать определённый сигнал, для определённого объекта?


Название: Re: QListWidget и война с currentItemChanged
Отправлено: GreatSnake от Апрель 15, 2010, 13:17
Дело в том, что при вызове setCurrentItem() вызывается QItemSelectionModel::setCurrentIndex() и уже потом модель вызывает нужные сигналы, которые зарядил QListWidget на эту модель. Заблокировав отработку сигналов для QListWidget вы не дали модели вызвать эти сигналы и сам QListWidget не узнал про изменение текущего элемента.

Код примерно такой:
Код
C++ (Qt)
 
void restoreCurrent()
{
    myList.setCurrentRow( myList.property( "current" ).toInt() );
    myList.setProperty( "current", -1 );
}
 
MySelectedChanged(QListWidgetItem * current, QListWidgetItem * previous)
{
   if( !previous || myList.property( "current" ).toInt() != -1 )
     return;
   myList.setProperty( "current", myList.row( previous ) );
   QTimer::singleShot( 0, this, SLOT( restoreCurrent() ) );
}
 
property() можно не использовать, а держать current_row в вашем классе не забыв инициализировать его в -1


Название: Re: QListWidget и война с currentItemChanged
Отправлено: GreatSnake от Апрель 15, 2010, 13:27
Если хотите сделать красиво, то отнаследуйтесь от QListWidget и перегрузите в нём currentChanged() метод:

Код
C++ (Qt)
void YourListView::currentChanged( const QModelIndex & current, const QModelIndex & previous )
{
   if( current.row() == 2 && QMessageBox::question() != QMessageBox::Yes )
     return;
   QListWidget::currentChanged( current, previous );
}
 


Название: Re: QListWidget и война с currentItemChanged
Отправлено: alexman от Апрель 15, 2010, 13:34
Если хотите сделать красиво, то отнаследуйтесь от QListWidget и перегрузите в нём currentChanged() метод:

Код
C++ (Qt)
void YourListView::currentChanged( const QModelIndex & current, const QModelIndex & previous )
{
   if( current.row() == 2 && QMessageBox::question() != QMessageBox::Yes )
     return;
   QListWidget::currentChanged( current, previous );
}
 
Наверное лучше назвать класс YourListWidget :) Хотя это не принципиально!


Название: Re: QListWidget и война с currentItemChanged
Отправлено: alexman от Апрель 15, 2010, 13:38
но как по мне - это не очень красиво, разве нет более симпотишного способа? :-)
Иногда поиск "красивого" способа не оправдан! Если можно обойтись двумя строчками кода лучше влепить две строчки кода (это по-моему) и не париться!


Название: Re: QListWidget и война с currentItemChanged
Отправлено: Igors от Апрель 15, 2010, 13:45
"Более симпатишный" вероятно был бы хорошо заметен в документации и назывался как-нибудь "bool canChangeSelection(..)" (хотя неясно было бы это в плюс или минус). Но поскольку его нет - сохранять предыдущий выбор придется так или иначе.


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 15, 2010, 17:08
попробовал реализовать вариант, который описал GreatSnake (который со свойствами).
Так у меня ничего и не получилось :-( виджет делает вид восстанавливает значение (вызывает пару раз сам себя) и в конечном итоге всё равно становится значением которое выделил пользователь, а не предыдущим значением :-(. ЗЫ: свойство я пробовал инициализировать как в -1, так и в 0 - результат одинаковый :-(... 


Название: Re: QListWidget и война с currentItemChanged
Отправлено: Marat(Qt) от Апрель 15, 2010, 20:33
Поведение нетипичное для обычного ListView и его стандартными методами, на мой взгляд, описывать его не правильно. В данном случае лучшим решением является переопределение метода(ов) как это предложил GreatSnake:
Если хотите сделать красиво, то отнаследуйтесь от QListWidget и перегрузите в нём currentChanged() метод:

Код
C++ (Qt)
void YourListView::currentChanged( const QModelIndex & current, const QModelIndex & previous )
{
   if( current.row() == 2 && QMessageBox::question() != QMessageBox::Yes )
     return;
   QListWidget::currentChanged( current, previous );
}
 

Т.е. если нужно получить нетипичное поведение виджета - ни в коем случае не начинайте "войну" с ним и его сигналами/слотами, меняйте их и все у вас получится.


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 16, 2010, 08:40
спасибо...
а можете ещё разъяснить что означает     
Код:
if( current.row() == 2 
это количество выделенных строк или индекс на выделенную строку?


Название: Re: QListWidget и война с currentItemChanged
Отправлено: GreatSnake от Апрель 16, 2010, 08:44
Индекс.
А вы что, ассистентом вообще не пользуетесь?


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 16, 2010, 08:54
пользуюсь, но сегодня тяжёлое утро :-) я ещё сплю....
спс, за ответы :-)


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 16, 2010, 14:35
вернёмся к нашим баранам!

решил решить проблему в лоб как говориться:
Код:
QMessageBox msg(QMessageBox::Question, "AXTUNG!!!", "Are you shure?", QMessageBox::Yes | QMessageBox::No);
if(msg.exec() == QMessageBox::No)
{
QBrush bcolor = current->background();
QBrush fcolor = current->foreground();

current->setBackground(previous->background());
previous->setBackground(bcolor);

current->setForeground(previous->foreground());
previous->setForeground(fcolor);

disconnect(&macList, SIGNAL(currentItemChanged ( QListWidgetItem * , QListWidgetItem * )), this, SLOT(ItemChanged(QListWidgetItem * , QListWidgetItem * )));
macList.setCurrentItem(previous);
macList.setCurrentRow(macList.row(previous));
macList.setItemSelected(previous, true);
macList.setItemSelected(current, false);

// macList.setCurrentIndex(macList.indexFromItem(previous));
connect(&macList, SIGNAL(currentItemChanged ( QListWidgetItem * , QListWidgetItem * )), this, SLOT(ItemChanged(QListWidgetItem * , QListWidgetItem * )));
}
label.setText(macList.currentItem()->text());

при помощи молотка и такой-то матери, а именно label.setText(macList.currentItem()->text()); выяснил, что текущее значение остаётся правильным, но вот подсветка переходит на последний выбранный элемент, уже пробовал танцы с бубном вокруг заднего фона - не помогло, ткните носом, что может ещё подсвечиваться? :-)


Название: Re: QListWidget и война с currentItemChanged
Отправлено: l_a_m от Апрель 16, 2010, 16:04
Ура товарищи! ЗАБОРОЛ!

если вдруг кому интересно пишу как:
Код:
//.срр
List::List(QWidget *parent)
: QWidget(parent)
{
macList.addItem("1");
macList.addItem("2");
macList.addItem("3");
previoseItem = macList.findItems("3", Qt::MatchFixedString)[0];
macList.setCurrentItem(previoseItem);
main.addWidget(&macList);
main.addWidget(&label);
this->setLayout(&main);
connect(&macList, SIGNAL(itemSelectionChanged()), this, SLOT(ItemChanged()));
}

void List::ItemChanged( )
{
QMessageBox msg(QMessageBox::Question, "AXTUNG!!!", "Are you shure?", QMessageBox::Yes | QMessageBox::No);
if(msg.exec() == QMessageBox::No)
{
if(previoseItem!=macList.currentItem())
{
disconnect(&macList, SIGNAL(itemSelectionChanged()), this, SLOT(ItemChanged()));
macList.setCurrentItem(previoseItem);
connect(&macList, SIGNAL(itemSelectionChanged()), this, SLOT(ItemChanged()));
}
}
else
{
previoseItem = macList.currentItem();
}
label.setText(macList.currentItem()->text());
}

//.h
class List : public QWidget
{
Q_OBJECT

public:
List(QWidget *parent =0);
~List();

private:
QVBoxLayout main;
QListWidget macList;
QLabel label;
QListWidgetItem *previoseItem;

private slots:
void ItemChanged (  );
};