Как работает ChartDataChangeEventListener?

Автор Kadet, 14 января 2022, 21:07

0 Пользователи и 1 гость просматривают эту тему.

Kadet

Добрый день!
Помогите разобраться с тем, как работает Listener - com.sun.star.chart.XChartDataChangeEventListener

В описаниях написано, что он отслеживает изменение данных или состояния ячеек Calc, диаграмм и т.п.

Макросом заношу данные в ячейки документа Calc и вешаю на эти ячейки этот слушатель.
Потом, макросом же, создаю в этом документе новый лист, не касаясь заполненных ячеек, но при этом почему-то начинают срабатывать листенеры, которые висят на предыдущем листе.

Почему же так происходит? На что они реагируют?

К тому же, никак не удаётся удалять этих слушателей, допустим если очистить лист и заполнить его новыми данными, то старые листенеры продолжают существовать. По-моему не помогает даже удаление листа, к которому они якобы привязаны.
А не удаётся их удалять потому что их много и все они созданы одним макросом и повешены на одну переменную. Так LO позволяет сделать, и все они работают, но для удаления каждого нужно чётко дать объект удаления, а при одинаковых названиях - даёт ошибку.

В общем, собрал маленькую простую демку.
Она заполняет 10 ячеек и на каждую вешает слушателя. Потом создаёт новый лист, но при его создании почему-то начинают срабатывать ранее установленные листенеры, привязанные к предыдущему листу, который никто не трогает. О чём сообщают сообщалки.

eeigor

#1
Глянул навскидку без запуска. Почему переменные объявлены как Public? Global.
Public Global ANCell
Public Global ANListener

Вы подключаете слушатель к каждой ячейке в цикле через вызов ANListenerSUB и перезаписываете переменную ANCell:
   ANCell = oSheet.getCellByPosition(oColumn,oRow)
Но, извините, ANCell - это не массив переменных для каждой ячейки, к которой подключается независимый слушатель.
Т.о., процедурой UnregisterANlistener Вы останавливаете последний запущенный слушатель.
Я не знаю по какой причине Вы не хотите подключить один слушатель ко всему диапазону ячеек...

com.sun.star.chart.XChartDataChangeEventListener
Метод disposing надо добавить тоже.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

Kadet

#2
Паблик только в демке. В оригинале глобал.

Года два назад уже крутил тему с листенерами. В том числе экспериментировал с массивами переменных. Только что-то пошло не так, а что конкретно уж не упомню. Эта проблема с выключением листенеров у меня крутится уже давно, но пока ещё не нашёл нормальный способ их отключения.
И понятно, что в переменной остаётся последний слушатель, но дело в том, что все они всё равно работают... в других версиях, где не нужно создавать дополнительные листы.

Диспозинг действительно нужно вставить. Всё размышлял нужен ли он или нет. Но... думаю не помешает.

Однако, вопрос всё таки в другом.

Kadet

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

Но, всё таки хотелось бы разобраться почему такое нечто случается. Хотя бы, чтобы не попадать в подобные ситуации в аналогичных случаях.

eeigor

#4
Я понял проблему.
Упростил ваш пример (прилагаю), оставив одну ячейку. Но при добавлении/удалении другого листа или перемещении любого листа возникает события слушателя изначально первого листа.
Работает с графиком (диаграммой) только или же с ячейками в том числе?
XChartDataChangeEventListenermakes makes it possible to receive events when chart data changes.
chartDataChanged is called whenever chart data changes in value or structure.

С другой стороны, chart data - это и есть диапазон ячеек.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#5
Цитата: eeigor от 15 января 2022, 00:31chartDataChanged is called whenever chart data changes in value or structure.
Я подготовил новый, облегченный пример. Ваш пример не годится для исследования.
Вероятно, что вставка/удаление или перемещение листа есть изменение структуры. Не знаю.
Однако Вы можете аккуратно отключить слушатель перед этими операциями и потом заново включить. Это если делать кнопкой (макросом). А если через интерфейс мышкой или в меню, то потребуется подключить XUndoManagerListener. Слишком сложно. Проще никак?

Во всяком случае подождем ответов других участников.

В прилагаемом примере обращение к листу осуществляется по имени листа.

Updated:
К примеру, при перемещении листа (любого, даже без изменения позиции листа с запущенным слушателем) запущенный слушатель теряется (но объектная переменная не пуста). Остановите (переменная будет сброшена) и заново запустите слушатель. Опять работает...
Теряется, как сказал, но опять таки не всегда. Вставил новый лист после второго (слушатель на первом) - и слушатель работает.

Я использую событие листа "Content changed", и мне хватает... Смотрите в сторону упрощения решения. Я предпочитаю принцип проектирования KISS (акроним для «Keep it simple, stupid»).
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

Kadet

#6
Цитата: eeigor от 15 января 2022, 01:13Проще никак?
Можно. Как описывал ранее, формировать структуру документа перед тем как развешивать слушателей. Тогда и Вкл/Выкл не потребуется. В итоге так и сделаю. По крайней мере это надёжней.
Но, проблема всё таки остаётся не разгаданной.
Видимо таки ChartDataChangeEventListener реагирует не только на изменения диапазонов, к которым привязан, но и на изменение структуры самого документа.
Цитата: eeigor от 15 января 2022, 00:31изначально первого листа.
Не факт. В оригинале слушателей вешаю начиная с третьего листа документа и при создании нового листа они реагируют. Далее не исследовал какие реагируют при создании следующих листов.

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

mikekaganski

#7
При добавлении листа генерируется событие "все формулы нужно пересчитать" (ScDocument::SetAllFormulasDirty), которое в т.ч. явно вызывает ScChartListenerCollection::SetDirty - все слушатели изменений диаграмм помечаются для оповещения, что и происходит по таймеру (то есть сразу после добавления).
С уважением,
Михаил Каганский

Kadet

Цитата: mikekaganski от 15 января 2022, 09:28Каждая ячейка - это ещё и диапазон, состоящий из одной ячейки.
Это понятно.
Цитата: mikekaganski от 15 января 2022, 09:28При добавлении листа генерируется событие ...
Т.е. от срабатывания слушателей при создании новых листов не уйти.
Ясно. Спасибо за пояснения!

Значит остаётся один путь - "нормальные герои всегда идут в обход".

mikekaganski

Надо просто понимать, что любой слушатель может срабатывать при многих разных условиях, не только при реальном изменении данных в его диапазоне. То есть сработка слушателя - это не событие "мои данные изменились", а событие "требуется обновление".
С уважением,
Михаил Каганский

Kadet

#10
Однако, с привязыванием листенеров к диапазонам снова засада, как и предполагалось. В итоге листенеры нужны не только для отслеживания самого факта изменений, но, и это главное, получение данных что изменилось и где. А при диапазонах сообщается лишь, что где-то внутри произошли некие изменения. А где и что изменилось - не сообщается. Или я плохо искал.

С занесением листенеров как переменных в массив тоже не всё так просто. Массив-то оно формирует, но при остановке помимо самой переменной листенера ещё требуется правильный диапазон, к которому он привязан. Т.е. массив должен быть как минимум двух-трёх мерный, где помимо самой переменной объекта листенера ещё должны храниться и адрес диапозона. А в идеале лучше сделать вообще отдельный тип, где первая подпеременая буд хранить RangeAdress, а вторая сам объект листенера.

Kadet

Цитата: mikekaganski от 15 января 2022, 09:50То есть сработка слушателя - это не событие "мои данные изменились", а событие "требуется обновление".
???

mikekaganski

Вы не описали, зачем Вам вообще слушатели. Для чего знать, какая именно ячейка изменилась. В принципе идея этого слушателя в том, чтобы получать данные о необходимости обновления для всего диапазона. Если для каждого диапазона объекты обновления разные, то нужны разные слушатели. Если обновляемый объект один - то и нет нужды знать, что именно изменилось внутри диапазона.
С уважением,
Михаил Каганский

mikekaganski

Цитата: Kadet от 15 января 2022, 10:11
???

И как отвечать на такое содержательное сообщение?
С уважением,
Михаил Каганский

Kadet

#14
Цитата: mikekaganski от 15 января 2022, 10:15
Цитата: Kadet от 15 января 2022, 10:11
???

И как отвечать на такое содержательное сообщение?
Это риторическое сообщение, ответа не требует. Означает - "озадачен".

Эти листенеры в моей программе нужны для следующего.
В некоей ячейке происходят некие изменения. Чаще всего я вешаю такие листенеры на ячейки со списками выбора. Допустим - "номенклатура материалов". По изменению данных в этой ячейке должен запуститься макрос, который получает изменённые данные, так же получает ГДЕ конкретно изменились эти данные. Затем эти новые данные как фильтр идут в SQL-запрос  БД, и оттуда получаются новые отфильтрованные данные. На выходе я получаю: список данных, из которых допустим нужно сформировать/переформировать список выбора в зависимой от изменённой ячейки ячейке... Либо получаю ID/коэф. или ещё т.п. из таблицы БД той позиции, которая появилась в измененной ячейке... Либо запускается макрос на пересчёт и вычисление значений для других ячейках, связанных с изменённой ячейкой....
В общем - случаев использования этого слушателя у меня масса.
(запутал, сам с трудом понимаю что написал)

Но, в общем и целом мне нужно:
- отловить момент произошедших в некоей ячейке изменений;
- получить новые данные из этой ячейки;
- получить адрес где конкретно произошли изменения.

Возможно я использую не тот листенер, но... другого способа не придумал.