Форум поддержки пользователей. LibreOffice, Apache OpenOffice, OpenOffice.org

Форум поддержки пользователей. LibreOffice, Apache OpenOffice, OpenOffice.org

20 Апрель 2021, 22:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
Новости: Часто задаваемые вопросы по LibreOffice и Apache OpenOffice.org
 
   Начало   Помощь Поиск Войти Регистрация    задать вопрос  
Страниц: 1   Вниз
  Печать  
Автор Тема: [Решено] Calc: Как сделать видимым начало диапазона после фильтрации?  (Прочитано 773 раз)
0 Пользователей и 1 Гость смотрят эту тему.
eeigor
Форумчанин
***
Offline Offline

Пол: Мужской
Сообщений: 514



« Стартовое сообщение: 3 Январь 2021, 16:58 »

День добрый и с Новым годом!
Есть диапазон условий (CriteriaRange) расширенного фильтра (AdvancedFilter) на листе и диапазон базы данных (DatabaseRange), фильтруемый макросом после изменения условий фильтрации. Строка заголовков столбцов данных закреплена. Проблема в том, что после наложения фильтра данные могут отображаться не с начала. А надо, чтобы с начала, с первой строки вновь отфильтрованных данных. Как прокрутить диапазон отфильтрованных данных в начало без изменения активной ячейки в диапазоне условий расширенного фильтра?
Код ниже решает задачу отображения отфильтрованных данных с первой видимой строки (.uno:GoToCell, .uno:GoDown), но теряется активная ячейка (напомню, что пользователь вводит условия в диапазоне условий). Вопрос касается исключительно удобства ввода данных в диапазоне условий.
Код:
Sub EnsureVisible()
   dim document as object
   dim dispatcher as object

   document   = ThisComponent.CurrentController.Frame
   dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

   ' Suppose $A$9 is the upper left corner of the database range.
   dim args1(0) as new com.sun.star.beans.PropertyValue
   args1(0).Name = "ToPoint"
   args1(0).Value = "$A$9"
   dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args1())
  
   dim args2(1) as new com.sun.star.beans.PropertyValue
   args2(0).Name = "By"
   args2(0).Value = 1
   args2(1).Name = "Sel"
   args2(1).Value = false
   dispatcher.executeDispatch(document, ".uno:GoDown", "", 0, args2())
end sub

Можно ли вообще прокрутить диапазон отфильтрованных данных к началу, не мешая работе пользователя, который последовательно вводит условия фильтра в диапазоне условий?
Данные фильтруются по событию листа  "Содержимое изменено" (OnChange Event).
Сейчас, если, к примеру, в результате наложения нового фильтра все видимые в данный момент строки будут скрыты, то можно вообще увидеть только зафиксированную строку заголовков столбцов. И ничего больше. Требуется прокрутить диапазон данных вверх.

Предположим, заголовки столбцов диапазона базы данных начинаются со строки 9.
Повторная фиксация этой строки вызывает прокрутку диапазона данных к началу, хотя и занимает некоторое время. При этом активная ячейка в диапазоне условий не меняется. Возможно, есть другой способ.
ThisComponent.CurrentController.freezeAtPosition (0, 9)  'ByName("A10")
« Последнее редактирование: 4 Январь 2021, 19:39 от eeigor » Записан

Ubuntu 18.04 LTS • LO 7.1.1.2 Community
rami
Гуру
*******
Offline Offline

Пол: Мужской
Сообщений: 3 068


iMac, LibreOffice и Apache OpenOffice


« Ответ #1: 3 Январь 2021, 20:46 »

Код:
ThisComponent.CurrentController.FirstVisibleRow = 50   'номер первой видимой строки
Записан

eeigor
Форумчанин
***
Offline Offline

Пол: Мужской
Сообщений: 514



« Ответ #2: 3 Январь 2021, 21:50 »

'номер первой видимой строки
Это не работает, поскольку на листе есть закреплённая строка: "Строка заголовков столбцов данных закреплена".
Во всяком случае я не знаю, как этим воспользоваться.

Строки 1-5 отведены для диапазона условий расширенного фильтра с заголовками в строке 1;
строка 7 - итоги по базе данных (SUBTOTAL);
строка 9 - заголовки полей базы данных, эта строка закреплена;
со строки 10 (индекс строки 9) идут сами данные диапазона базы данных.
Строки 6 и 8 оставлены пустыми.

Сработала, как писал выше, такая строка:
   ThisComponent.CurrentController.freezeAtPosition(0, 9)
То есть повторное закрепление строки заголовков вызывает прокрутку отфильтрованных данных к началу диапазона.
Но будет ли такой способ оптимальным, rami?

Диапазон отфильтрованных на скриншоте данных начинается со строки 57. Но так как диапазон данных в ходе предыдущей работы с ним был прокручен к концу, то после наложения нового фильтра (полей вправо много, и условий фильтра не видно) отображена первая строка 1910, а надо, разумеется, прокрутить к началу - строке 57. Если использовать метод 'select', то теряется активная ячейка в диапазоне условий, и это неудобно.

А пользователь, работая с диапазоном условий, может нажать как TAB (переход вправо), так и ENTER (переход вниз). В общем, активизация ячеек в диапазоне базы данных (DatabaseRange) мешает работе пользователя, ибо активной ячейкой в момент срабатывания события является исходная, а к какой вернуться (перейти) - зависит от ранее нажатой клавиши... При этом не хочется усложнять код.

UPD: Некоторые замечания по сравнению с работой расширенного фильтра Excel и о самом LO Calc AdvancedFilter
Условия в диапазоне условий в Excel должны вводится только подряд, и там я использовал .CurrentRegion для уточнения размера диапазона условий перед его передачей методу диапазона .AdvancedFilter. В LO Calc это не имеет значения. Можно вводить условия в любую строку, хоть в 5-ю, то есть не подряд. Странно однако... Зато удобно!

Кроме того, обнаружена ошибка (возможно, баг).
В LO Calc диапазон базы данных (DatabaseRange), в отличие от обычного именованного диапазона, уже имеет свой дескриптор фильтра (FilterDescriptor) и, соответственно, не имеет метода filter, зато у него имеется метод refresh. Однако к диапазону базы данных можно обратиться и как к обычному диапазону по имени. Далее создать независимый дескриптор фильтра, связать его с диапазоном условий и передать диапазону данных. В этом случае у диапазона данных будет метод filter. Этот способ рассматривается здесь на форме (пример от rami для AdvancedFilter).

Другой вариант сложнее: использовать встроенный дескриптор фильтра объекта базы данных (DatabaseRange.FilterDescriptor), примеров нет ни здесь, ни у Питоньяка. Дело усложняется и тем, что некоторые объекты предоставляют копии своих свойств, и значения этим свойствам нельзя устанавливать напрямую. В результате процесс инициализации свойств усложняется и непонятен.

Вот так работает (зд. имена переменных говорят сами за себя):
    oDescriptor = oDBRange.FilterDescriptor
    oFields = oCriteriaRange.createFilterDescriptorByObject(oDBRange.ReferredCells).FilterFields
    oDescriptor.FilterFields = oFields

А вот так у меня не работает:
    oFields = oCriteriaRange.createFilterDescriptorByObject(oDBRange.ReferredCells).FilterFields
    oDBRange.FilterDescriptor.FilterFields = oFields
Вот не смог я установить .FilterDescriptor.FilterFields напрямую!? Могу ошибаться. Увы, я не программист и не понимаю смысл этой "концепции".

Однако использование встроенного дескриптора фильтра выглядит логичнее, не правда ли?
В первом случае мы вызываем метод filter, во втором - метод refresh, поскольку у объекта DatabaseRange нет метода filter.

А теперь о баге. В первом случае в диапазоне данных могут отображаться кнопки автофильтра, и он не мешает работе расширенного фильтра. Во втором случае мешает, расширенный фильтр не работает через вызов метода refresh, но работает-таки через меню расширенного фильтра LO Calc (Данные/Ещё фильтры/Расширенный фильтр...).
Лечится просто:
    If oDBRange.AutoFilter Then oDBRange.AutoFilter = False
Вот только локализовать ошибку удалось с большим трудом и не сразу! Ибо Calc "молчит, как партизан".

Два способа обратиться к диапазону базы данных, где "Database" есть имя диапазона:
    oDataRange = ThisComponent.CurrentController.ActiveSheet.getCellRangeByName("Database")  '<<< oDataRange.filter()
    oDBRange = ThisComponent.DatabaseRanges.getByName("Database")  '<<< oDBRange.refresh()
При этом заметьте, что в окне "Управление именами" нет имени "Database". А если вы его создадите (продублируете и тут), то у вас не будут работать структурные ссылки (см. ниже).

UPD2: А зачем вообще работать с диапазонами базы данных?
Лично мне периодически приходится обрабатывать "сырые" данные, и использование структурных ссылок (с использованием квадратных скобок, кто знает) при обращении к полям диапазона базы данных позволяет не создавать временных именованных диапазонов. Это очень наглядно и легко. Однако после повторного открытия файла структурные ссылки будет заменены на абсолютные. Но в моём случае это неважно.


* Снимок экрана от 2021-01-03 21-50-09.png (13.44 Кб, 240x283 - просмотрено 9 раз.)
« Последнее редактирование: 4 Январь 2021, 07:24 от eeigor » Записан

Ubuntu 18.04 LTS • LO 7.1.1.2 Community
sokol92
Форумчанин
***
Offline Offline

Пол: Мужской
Сообщений: 379


WWW
« Ответ #3: 4 Январь 2021, 12:42 »

не понимаю смысл этой "концепции".
Добрый день!
Книга Питоньяка OOME_4_0.odt, комментарии к листингам 196 и 411. Понять, действительно, не просто. Улыбка
Записан

Владимир.
eeigor
Форумчанин
***
Offline Offline

Пол: Мужской
Сообщений: 514



« Ответ #4: 4 Январь 2021, 15:16 »

Понять, действительно, не просто.
sokol92, спасибо за примеры; я увидел. Выше, как оказалось, я поступил с объектом Filelds дескриптора фильтра по аналогии с примером у Питоньяка:
Код:
Dim aLocale
aLocale = oCursor.CharLocale  'Or use a copy
aLocale.Language = "fr"  'Set Locale to use the French language
aLocale.Country = "CH"  'Set Locale to use Switzerland as the country
oCursor.CharLocale = aLocale  'Assign the value back
« Последнее редактирование: 4 Январь 2021, 15:18 от eeigor » Записан

Ubuntu 18.04 LTS • LO 7.1.1.2 Community
eeigor
Форумчанин
***
Offline Offline

Пол: Мужской
Сообщений: 514



« Ответ #5: 4 Январь 2021, 16:36 »

ThisComponent.CurrentController.FirstVisibleRow = 50   'номер первой видимой строки
Да, это верный метод, только требуется уточнить окно ("ScViewPaneObj" object), в котором это делается:

    ' Зд. строка с индексом 9 идет сразу под зафиксированной строкой заголовков и относится к окну 2 (с индексом 1).
    ThisComponent.CurrentController.getByIndex(1).setFirstVisibleRow(9)  '.FirstVisibleRow = 9

Но так тоже работает:
    ThisComponent.CurrentController.freezeAtPosition(0, 9)  'повторно фиксирует зафиксированную строку

Хотя первый способ более осмысленный.
« Последнее редактирование: 4 Январь 2021, 16:55 от eeigor » Записан

Ubuntu 18.04 LTS • LO 7.1.1.2 Community
Страниц: 1   Вверх
  Печать  
 
Перейти в:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!