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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?  (Прочитано 12876 раз)
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« : Январь 19, 2009, 18:00 »

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

Есть у меня виджет класса QListView. И вот нужно сделать таку вещь - вызывать некую функцию при любом изменении состояния виджета и его selection модели. Ну, тоесть

- Сдвинул пользователь указатель клавишами - вызвать функцию.
- Сдвинул мышой - вызвать функцию.
- Выбирает несколько строк - при каждом добавлении строки вызвать функцию.
- Обновились все данные в листе - вызвать функцию.
- Обновились какие-то данные в листе, одна строка - вызвать функцию.
И т.д.

Сейчас я использую сигналы на все случаи жизни, примерно вот так

Код:
 // Сигналы для обновления панели инструментов при изменении в selectionModel()
 connect(recordview->selectionModel(), SIGNAL(currentChanged (const QModelIndex&, const QModelIndex&)),
         this, SLOT(tools_update(void)));
 connect(recordview->selectionModel(), SIGNAL(selectionChanged (const QItemSelection&, const QItemSelection&)),
         this, SLOT(tools_update(void)));

 // Сигналы для обновления панели инструментов
 connect(recordview, SIGNAL(activated(const QModelIndex &)),
         this, SLOT(tools_update(void)));
 connect(recordview, SIGNAL(clicked(const QModelIndex &)),
         this, SLOT(tools_update(void)));
 connect(recordview, SIGNAL(doubleClicked(const QModelIndex &)),
         this, SLOT(tools_update(void)));
 connect(recordview, SIGNAL(entered(const QModelIndex &)),
         this, SLOT(tools_update(void)));
 connect(recordview, SIGNAL(pressed(const QModelIndex &)),
         this, SLOT(tools_update(void)));

И происходит следующие. При некоторых действиях вызывается функция tools_update() один раз, но при при других - два раза подряд, при третьих - три раза подряд. А нужно, чтоб один раз. Как этого добиться?
Записан

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

Сообщений: 2901



Просмотр профиля WWW
« Ответ #1 : Январь 19, 2009, 18:26 »

Вы заканектили все имеющиеся сигналы к одному слоту. На оперделенное действие может испускаться 1-3 разных сигнала, вот и получаем такое поведение. Выберите только те сигналы, которые действительно нужны для работы.
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #2 : Январь 19, 2009, 18:55 »

Вы заканектили все имеющиеся сигналы к одному слоту. На оперделенное действие может испускаться 1-3 разных сигнала, вот и получаем такое поведение.
Ну это понятно.

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

Собираю информацию по крупицам
http://webhamster.ru
Dendy
Гость
« Ответ #3 : Январь 19, 2009, 19:08 »

А что этот слот делает хоть?
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #4 : Январь 19, 2009, 20:43 »

А что этот слот делает хоть?

Он управляет доступностью кнопок на панельке действий с элементами списка.

Ну то есть, если выбрано несколько строк, то кнопка редактирования блокируется, т.к. пользователю непонятно, какой элемент будет редактироваться. Или если вообще не выбран ни один элемент, то кнопка удаления недоступна, ибо нечего удалять. Или если курсор находится в самом верху списка, то кнопка переноса строки вверх неактивна, т.к. некуда переносить. Ну и т.д.
Записан

Собираю информацию по крупицам
http://webhamster.ru
Pretorean
Гость
« Ответ #5 : Январь 19, 2009, 21:07 »

как вариант, можно сделать так, чтобы этот слот срабатывал не чаще чем раз 500 милисекунд
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #6 : Январь 19, 2009, 21:20 »

Согласен с предыдущим ответом - вообще отвязать его от сигналов и только по таймеру вызывать
Записан
_OLEGator_
Гость
« Ответ #7 : Январь 19, 2009, 21:29 »

А разве события currentChanged и selectionChanged не будут оба происходить?
Помоему можно отслеживать только selectionChanged и уже при изменении выбора проверять сколько выделено элементов (selectedIndexes()) и делать соответствующие изменения...
Та же ситуация с activated и clicked...
Записан
Dendy
Гость
« Ответ #8 : Январь 19, 2009, 21:30 »

Код:
class MyWindow ...
{
  QBasicTimer updateTimer_;
};

void MyWindow::tools_update()
{
  if ( !updateTimer_.isActive() )
    updateTimer_.start( 0, this );
}

void MyWindow::timerEvent( QTimerEvent * e )
{
  if ( e->timerId() == updateTimer_.timerId() )
  {
    updateTimer_.stop();
    // real update goes here
    return;
  }
}

Но есть одно но. Такой подход нарушает синхронность операций. Порядок разбора событий на каждой итерации неопределён. Следовательно, если юзер успел кликнуть мышью по кнопке, - она может нажаться и только потом выключиться, так как тело timerEvent() выполнится позже. Другими словами вам прийдётся самому проверять в слотах корректно ли было нажатие на кнопку или нет.
Записан
Rcus
Гость
« Ответ #9 : Январь 19, 2009, 21:42 »

Вот нашел кое-что.
Цитировать
QCoreApplication::removePostedEvents ( QObject * receiver, int eventType )   [static]
В слоте привязанном ко все сигналам вызываем postEvent (допустим в класс окна), переопределяем обработчик event() (ну или перехватчиком), отсеиваем наше событие и вызываем очистку очереди событий для данного типа событий (таким образом за один проход петли сообщений нужные действия будут произведены только один раз)
Записан
Dendy
Гость
« Ответ #10 : Январь 19, 2009, 21:56 »

Вот нашел кое-что.

Это всё будет работать только если события положены в стек, а у топикстартера по всей видимости они соединены с помощью Qt::DirectConnection. Кроме того для такой очистки мы должны контролировать цикл событий. Кроме того мы должны создать собственное событие. Вобщем залезть в дебри ради простой вещи.
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #11 : Январь 19, 2009, 22:59 »

А нет ли какого способа определять переключение основного программного цикла? Тогда в методе можно былоб прописать блокировку, что если в текущем цикле метод уже вызывался, то сразу выход без выполнения последущего кода.
Записан

Собираю информацию по крупицам
http://webhamster.ru
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #12 : Январь 20, 2009, 01:56 »

И все же какие есть но против использования таймера?

Предположим пользователь действительно успеет нажать на кнопку кот. на самом деле вот- вот запретиться (при след. вызове таймера) - и скажем он нажмет удалить запись когда ничего не выделено. Но обработчик (скажем процедура deleteRows() ) ведь так и так должен проверять и делать return в таком случае.

И вообще эта ситуация будет крайне мала даже если таймер будет всего лишь 500 мс. - а если еще меньше то тем более. Если в коде метода кот. обновляет состояние контролов  ( UpdateControlsState() ) не будет никаких долгих операций - а их не должно быть по опр. т.к. все от чего зав-сят сост-я контролов кешируемые значения (число строк, номер строки и т.д.) - за ними не надо лезть в БД или на диск.

Тогда изобретать что-то сверхестественное просто смысла нет.

Еще вариант в всех методах кот. манипулируют с данными - вставка, удаление, подвинуть вверх/вних и т.д. - в конце использовать updateControlsState() - тогда ни таймер не нужен ни сигналы.
Записан
Dendy
Гость
« Ответ #13 : Январь 20, 2009, 02:24 »

И все же какие есть но против использования таймера?

Дело в том что даже если таймер сработает на следующей итерации - пользователь таки успеет нажать. Простейший пример: ОС запустит сборку мусора, и пока интерфейс программы подзамерз - юзер, проклиная программистов, успел нервно натыкать мышью по окнам, после чего в программе выгребается список накопленых событий. Проблема даже не скорости чтобы лишний раз проверить - это даже за задержку нельзя считать. Дело в том, что вам прийдётся дублировать функционал в программе, следить в нескольких местах за одним и тем же состоянием, следовательно - возможность ошибиться при написании кода увеличивается. Через время вы потенциально будете дописывать код и случайно забудете добавить проверку, понадеявшись что кнопка просто не может быть нажать в этой ситуации.

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

Так что автор правильно мыслит. Я тоже задавался этим вопросом, думал запоминать номер текущей итерации цикла событий, но ничего подобного в Qt не нашёл. Я бы посоветовал разбить фнукционал tools_update() на несколько составляющих и коннектить различные сигналы к различным слотам. Или минимизировать уже существующие коннекты.
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #14 : Январь 20, 2009, 03:45 »

Тогда в чем проблема если есть метод - принадлежащий форме на кот. отобр. данные

void updateCtrlsState()
{
    ui.btnAddRow->setEnabled( некот. условие );
    ui.btnDelRow->setEnabled( rowCount() > 0 );
    и т.д.
};

-- вначале на форме все кнопки Disabled
-- первый вызов этого метода после открытия формы и/или выполнения запроса,
далее во всех обработчиках куда может сунуться пользователь т.е.

on_btnAddRow_pressed()
{
    .......собственно действия
    updateCtrlsState();
}

Да такая конструкция требует скурпулезной расстановки (последнее время я изначально делаю по таймеру а уже если остается время перевожу на такую систему) по всему коду пользовательского интерфейса, но связь всех сигналов - это в целом тоже самое только в менее явном виде, что в рез-те может дать возм. нажать п-лю НЕРАЗРЕШЕННЫЕ кнопки.

Если вы считаете что есть потенциальная возм. что даже так во время выполнения ".......собственно действия" - система замерзнет и "updateCtrlsState();" - еще не запретит кнопки - то ведь равноценно и пользователь просто тыкнет мышкой в якобы разрешенную кнопку - но она не нажмется на самом деле т.к. интерфейс тоже заморожен - и вероятнее всего когда система "отмерзнет" и начнет выполнение накопившихся событий "updateCtrlsState();" выполнится первым. --- Если есть потенциальная возм. что он первым не выпо-ся то сомневаюсь что стоит беспокоиться об этом т.к. вероятность такого случая уж слишком мала.

По поводу того что проверка является костылем - предположим модель пишется отдельными программистами, а интерфейс другими. Почему создатели модели при выполнении deleteRow(153); не могут обработать и проанализировать что если такой строки нет(153) то не "Падать программе" из-за принципа неделания лишних проверок - а просто сделать return. Да нажатие не той кнопки тупым пользователем и созерцание того что ничего не произошло вызовет у него недоумени - но падение программы вообще приведет к потере данных (какая нибудь накладная пропадет) - и он тогда вообще обозлится (пользователь) т.к. надо будет вбивать ее заново из-за того что он не вовремя на кнопку нажал. Если согласиться с эти - то даже нажатие "запрещенной" кнопки ничего не вызовет, а на след. проходе событий она запретится - и поль-ль может сообразит что делает глупость.

Надеюсь никого не обидел - но я использую оба подхода и пока в завершенных частях программы проблем нажатия "запрещенных" кнопок не было. Или мне о них не сообщают т.к. "накладная не теряется". Смеющийся
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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