Хранение и отображение иерархической (древовидной) структуры объектов

Автор iyugov, 5 мая 2021, 18:55

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

iyugov

Есть необходимость хранить в базе данных иерархию объектов - тем учебных заданий. У каждой темы может быть произвольное количество подчинённых тем, у тех - тоже подчинённые темы и так далее. Колчество уровней подчинения не ограничено.

Для хранения тем в базе данных создана таблица со следующими полями:
1) id - ключ;
2) title - название объекта;
3) parent_id - id объекта из этой же таблицы.
Для id и parent_id создана связь.

Не могу понять, как организовать просмотр этой структуры на форме Base. Сейчас там форма с субформой, где для каждой темы видны непосредственно подчинённые темы (см. 1-й скриншот). Но такой формой в большинстве случаев совершенно невозможно пользоваться. Очень желательно именно древовидное отображение тем (пример - скриншот №2). Можно ли получить в LibreOffice Base древовидное отображение данных из таблицы? Искал подобие TreeView, но в формах Base такой элемент управления вроде бы недоступен.

Прошу форум помочь с отображением иерархических данных на форме.

eeigor

На вашем рисунке показан пример элемента управления "Hierachical Grid" (иерархическая сетка). Заполняется как TreeView (рекурсивно при неограниченной вложенности). Есть ли здесь такой (от третьей стороны)?..
Есть служба: UnoControlGridModel. Но это для диалоговых окон.
XGridDataModel   Задает XGridDataModel, предоставляющий иерархические данные.

Upd1
Link

В MS Access я размещал обычный TreeView прямо на форме и синхронизировал его с записью в главной форме. Приемлемо вполне. Кстати, там можно было и переподчинять узлы методом Drag&Drop.
Есть там и иерархический Recordset (ADODB) с своей SQL конструкцией (оператор SHAPE)... Тоже интересно познакомиться с тем, как эти вопросы решаются здесь...
Возможно, вам надо реализовать работу со структурой в диалоговом окне (просмотр и выбор), а все поля иметь в штатной форме (ввод данных). Текущий узел в диалоговом окне можно синхронизировать с записью в форме. Как заполнить дерево – ссылка выше. Только рекурсивно. Загружать первый дочерний уровень, а его узлам, в свою очередь, подцеплять фиктивный (dummy) узел для каждой вложенной ветви (чтобы отображался знак "плюс"); при разворачивании такого узла производится загрузка по аналогии требуемой ветви. Если записей до 1000, то можно загрузить всё дерево сразу (без фиктивных узлов).
В LO я этого всего ещё не делал...

Upd2
Обратите внимание на свойство "hasChildsOnDemand" (если True, хоть и ChildCount = 0, то ветвь в источнике не пустая). Я так понимаю, что не надо цеплять фиктивный узел: достаточно задать свойство. Много всего...
http://www.openoffice.org/api/docs/common/ref/com/sun/star/awt/tree/XTreeExpansionListener.html#requestChildNodes
Поделитесь результатами

Upd3
Link
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

iyugov

Сделал форму topic_selection, диалог, заполнение дерева (пока тупенько и без рекурсии).
Видно, что дерево заполняется медленно (там 500+ узлов), но это пока не главная проблема, не связываюсь с OnDemand.
Вопросы такие:
- как возвращать из диалога значение выбранного узла?
- можно ли закрывать диалог при double-click на узле?

Пока непонятно, насколько удобно будет этим пользоваться.

eeigor

Это очень замедляет:
ReDim Preserve Nodes(N...)
Сделайте запрос к БД и, оперируя точными значениями, инициализируйте массив сразу.
Я, правда, не понял смысла этого:
      If UBound(Nodes()) = N Then
         If N < 1024 Then
            ReDim Preserve Nodes(N * 2)
         Else
            ReDim Preserve Nodes(N + 1024)
         End If
      End If

If you are interested in knowing when the selection changes implement a ::com::sun::star::view::XSelectionChangeListener and add the instance with the method ::com::sun::star::view::XSelectionSupplier::addSelectionChangeListener(). You than will be notified for any selection change.
Или это (надо посмотреть):
getNodeForLocation   Returns the node at the specified location.

MsgBox(oTreeCtrl.getSelection.getDisplayValue())

Link

На оба ваши вопроса даю (чисто умозрительно) положительные ответы. Но теперь вы ведущий, а мы наблюдатели.
Думаю, что путь выбран правильно.
Цитата: iyugov от  6 мая 2021, 22:08Пока непонятно, насколько удобно будет этим пользоваться.
Удобно. Окно диалога с деревом можно и не закрывать, если сделать его соответствующего размера (в зависимости от длины наименований).

Вам ещё один пример в помощь (прикреплен). Загружает дерево каталогов молниеносно. Выполните процедуру Main и выберите папку где-то верхнего уровня.


Upd:
Обратите внимание на события мыши (mousePressed, щелчки мыши тоже определяются: см. здесь), а также событие mouseDragged (перетаскивание).
Не отформатировано. Примеров использования нет. Я это вижу впервые...

__ com.sun.star.awt.tree.XTreeEditListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
nodeEditing                                                com.sun.star.awt.tree.XTreeEditListener
nodeEdited                                                 com.sun.star.awt.tree.XTreeEditListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.tree.XTreeExpansionListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
requestChildNodes                                          com.sun.star.awt.tree.XTreeExpansionListener
treeExpanding                                              com.sun.star.awt.tree.XTreeExpansionListener
treeCollapsing                                             com.sun.star.awt.tree.XTreeExpansionListener
treeExpanded                                               com.sun.star.awt.tree.XTreeExpansionListener
treeCollapsed                                              com.sun.star.awt.tree.XTreeExpansionListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XFocusListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
focusGained                                                com.sun.star.awt.XFocusListener
focusLost                                                  com.sun.star.awt.XFocusListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XKeyListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
keyPressed                                                 com.sun.star.awt.XKeyListener
keyReleased                                                com.sun.star.awt.XKeyListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XMouseListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
mousePressed                                               com.sun.star.awt.XMouseListener
mouseReleased                                              com.sun.star.awt.XMouseListener
mouseEntered                                               com.sun.star.awt.XMouseListener
mouseExited                                                com.sun.star.awt.XMouseListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XMouseMotionListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
mouseDragged                                               com.sun.star.awt.XMouseMotionListener
mouseMoved                                                 com.sun.star.awt.XMouseMotionListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XPaintListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
windowPaint                                                com.sun.star.awt.XPaintListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.awt.XWindowListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
windowResized                                              com.sun.star.awt.XWindowListener
windowMoved                                                com.sun.star.awt.XWindowListener
windowShown                                                com.sun.star.awt.XWindowListener
windowHidden                                               com.sun.star.awt.XWindowListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.lang.XEventListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.util.XModeChangeApproveListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
approveModeChange                                          com.sun.star.util.XModeChangeApproveListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.util.XModeChangeListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
modeChanged                                                com.sun.star.util.XModeChangeListener

  _ Methods using this Listener as a parameter _    ( nothing to display )


__ com.sun.star.view.XSelectionChangeListener __

  _ Events watched by this Listener _
disposing                                                  com.sun.star.lang.XEventListener
selectionChanged                                           com.sun.star.view.XSelectionChangeListener

  _ Methods using this Listener as a parameter _    ( nothing to display )

Updated
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

iyugov

Такой вариант ReDim - сначала экспоненциальное выделение памяти, потом линейное. В каких-то языках видел подобное - компромисс между частыми выделениями памяти и чрезмерным запасом. Да, лучше запросом получить количество сразу. Правда, если поставить сразу достаточный размер, то задержка всё равно сохранится.

Спасибо за наводки. Буду думать. Пробую несколько вариантов, в том числе с макросами на Python и, возможно, с вынесением части интерфейса в отдельный скрипт. LibreOffice в основном интересен как сервер отображения самих заданий, автоматической генерации и конвертации документов. В том, что не касается содержимого заданий, возможны варианты. К лету не жалко поэкспериментировать, но чем больше удачный выбор сэкономит времени в следующем учебном году, тем лучше.

iyugov

Пока остановился всё-таки на отображении в форме, с помощью запроса с рекурсией. Надо идти дальше.

economist

Выглядит вполне понятно, но все же не деревом.

Если же смотреть в сторону Python - контрол Treeview для tkinter выглядит лучше, поддерживает цвета, форматы, "зебру", под-деревья в другом окне.

В "LO-родном" Python - его вырезали, и через докачанный pip он не поставится. Но можно скопировать из установленного внешнего той же версии/разрядности: https://tk-tutorial.readthedocs.io/en/latest/tree/tree.html (у меня заработал даже из 3.8)
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

iyugov

Тем оказалось уже 850, на 7 уровней вложенности. Оказалось, что такой список работает быстро, и подходящие темы в нём ищутся с меньшим числом телодвижений: вся иерархия уже раскрыта и видна, видны темы на соседних уровнях, вроде бы поддаётся фильтрации, можно вынести название из нижнего уровня вперёд и искать набором на клавиатуре. Вполне эффективный вариант получился. Кажется, он для моих целей удобнее, чем TreeView в принципе. А ещё он прямо на форме расположен, а на ней и без того есть над чем подумать, одной проблемой меньше.