Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: xintrea от Январь 19, 2009, 18:00



Название: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: xintrea от Январь 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() один раз, но при при других - два раза подряд, при третьих - три раза подряд. А нужно, чтоб один раз. Как этого добиться?


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: pastor от Январь 19, 2009, 18:26
Вы заканектили все имеющиеся сигналы к одному слоту. На оперделенное действие может испускаться 1-3 разных сигнала, вот и получаем такое поведение. Выберите только те сигналы, которые действительно нужны для работы.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: xintrea от Январь 19, 2009, 18:55
Вы заканектили все имеющиеся сигналы к одному слоту. На оперделенное действие может испускаться 1-3 разных сигнала, вот и получаем такое поведение.
Ну это понятно.

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


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Январь 19, 2009, 19:08
А что этот слот делает хоть?


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: xintrea от Январь 19, 2009, 20:43
А что этот слот делает хоть?

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

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


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Pretorean от Январь 19, 2009, 21:07
как вариант, можно сделать так, чтобы этот слот срабатывал не чаще чем раз 500 милисекунд


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: break от Январь 19, 2009, 21:20
Согласен с предыдущим ответом - вообще отвязать его от сигналов и только по таймеру вызывать


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: _OLEGator_ от Январь 19, 2009, 21:29
А разве события currentChanged и selectionChanged не будут оба происходить?
Помоему можно отслеживать только selectionChanged и уже при изменении выбора проверять сколько выделено элементов (selectedIndexes()) и делать соответствующие изменения...
Та же ситуация с activated и clicked...


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Январь 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() выполнится позже. Другими словами вам прийдётся самому проверять в слотах корректно ли было нажатие на кнопку или нет.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Rcus от Январь 19, 2009, 21:42
Вот нашел кое-что.
Цитировать
QCoreApplication::removePostedEvents ( QObject * receiver, int eventType )   [static]
В слоте привязанном ко все сигналам вызываем postEvent (допустим в класс окна), переопределяем обработчик event() (ну или перехватчиком), отсеиваем наше событие и вызываем очистку очереди событий для данного типа событий (таким образом за один проход петли сообщений нужные действия будут произведены только один раз)


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Январь 19, 2009, 21:56
Вот нашел кое-что.

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


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: xintrea от Январь 19, 2009, 22:59
А нет ли какого способа определять переключение основного программного цикла? Тогда в методе можно былоб прописать блокировку, что если в текущем цикле метод уже вызывался, то сразу выход без выполнения последущего кода.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: break от Январь 20, 2009, 01:56
И все же какие есть но против использования таймера?

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

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

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

Еще вариант в всех методах кот. манипулируют с данными - вставка, удаление, подвинуть вверх/вних и т.д. - в конце использовать updateControlsState() - тогда ни таймер не нужен ни сигналы.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Январь 20, 2009, 02:24
И все же какие есть но против использования таймера?

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

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

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


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: break от Январь 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. Да нажатие не той кнопки тупым пользователем и созерцание того что ничего не произошло вызовет у него недоумени - но падение программы вообще приведет к потере данных (какая нибудь накладная пропадет) - и он тогда вообще обозлится (пользователь) т.к. надо будет вбивать ее заново из-за того что он не вовремя на кнопку нажал. Если согласиться с эти - то даже нажатие "запрещенной" кнопки ничего не вызовет, а на след. проходе событий она запретится - и поль-ль может сообразит что делает глупость.

Надеюсь никого не обидел - но я использую оба подхода и пока в завершенных частях программы проблем нажатия "запрещенных" кнопок не было. Или мне о них не сообщают т.к. "накладная не теряется". ;D


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Январь 20, 2009, 05:33
2 break

В программировании нет маленькой вероятности, большой вероятности, очень маленькой вероятности и т. п. Вероятность в программировании определяется булевской переменной: или она есть, или её нет. Никаких "сомнений" быть не может. Вы описали проблему и решили её неправильно. Или вы думаете что ассерты в программах придумали параноики?

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

P.S. Дальнейший оффтоп на эту тему не приветствуется. Просьба по существу.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Danila_Bagrofff от Декабрь 11, 2009, 09:39
а почему нельзя просто использовать обработчики событий?

хотя... тоже ведь события нужно отбирать так, чтобы они не дублировали друг друга при вызове функций?

Но здесь, по-моему, проще отобрать нужный список событий, чтобы не было дублежа и вызывать функции необходимые по данному действию...


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: spectre71 от Декабрь 11, 2009, 09:55
Вы заканектили все имеющиеся сигналы к одному слоту. На оперделенное действие может испускаться 1-3 разных сигнала, вот и получаем такое поведение.
Ну это понятно.

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

Реакция на любое действие - бессмысленна.
Определи сперва на какие действия должна быть реакция. После этого уже прочитай описание сигналов и выбери необходимое подмножество, а не складывай все подряд в кучу.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Igors от Декабрь 11, 2009, 13:44
Хочется избавиться от утомительного анализа "а не было ли сначала др. сигнала (старшего)?" Разбить функционал tools_update на части можно, но проблема остается. Я бы сначала в tools_update прикинул  не является ли действие холостым, т.е. уже выполнено предыдущими действиями (хранил бы какие-то "текущие" данные и сравнивал их с пришедшими). Если совпадают - не лезть к диску, не обновлять UI а просто выйти.  Вообще чем занимается tools_update?


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: Dendy от Декабрь 12, 2009, 04:21
Вообще чем занимается tools_update?

Мне кажется автор оригинального кода или решил проблему или давно про неё забыл.


Название: Re: Сигнал "что-то изменилось". Как вместо 10 сигналов сделать 1?
Отправлено: SASA от Декабрь 15, 2009, 11:26
Извините за офтоп. Но мимо такого пройти не мог.

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

Спорно, а зачастую не верно.