Есть необходимость хранить в базе данных иерархию объектов - тем учебных заданий. У каждой темы может быть произвольное количество подчинённых тем, у тех - тоже подчинённые темы и так далее. Колчество уровней подчинения не ограничено.
Для хранения тем в базе данных создана таблица со следующими полями:
1) id - ключ;
2) title - название объекта;
3) parent_id - id объекта из этой же таблицы.
Для id и parent_id создана связь.
Не могу понять, как организовать просмотр этой структуры на форме Base. Сейчас там форма с субформой, где для каждой темы видны непосредственно подчинённые темы (см. 1-й скриншот). Но такой формой в большинстве случаев совершенно невозможно пользоваться. Очень желательно именно древовидное отображение тем (пример - скриншот №2). Можно ли получить в LibreOffice Base древовидное отображение данных из таблицы? Искал подобие TreeView, но в формах Base такой элемент управления вроде бы недоступен.
Прошу форум помочь с отображением иерархических данных на форме.
На вашем рисунке показан пример элемента управления "Hierachical Grid" (иерархическая сетка). Заполняется как TreeView (рекурсивно при неограниченной вложенности). Есть ли здесь такой (от третьей стороны)?..
Есть служба: UnoControlGridModel. Но это для диалоговых окон.
XGridDataModel Задает XGridDataModel, предоставляющий иерархические данные.
Upd1
Link (https://wiki.openoffice.org/wiki/Going_further_with_Dialog_and_Component#The_New_Tree_Control)
В 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 (https://wiki.openoffice.org/wiki/Treecontrol#XTreeWillExpandListener)
Сделал форму topic_selection, диалог, заполнение дерева (пока тупенько и без рекурсии).
Видно, что дерево заполняется медленно (там 500+ узлов), но это пока не главная проблема, не связываюсь с OnDemand.
Вопросы такие:
- как возвращать из диалога значение выбранного узла?
- можно ли закрывать диалог при double-click на узле?
Пока непонятно, насколько удобно будет этим пользоваться.
Это очень замедляет:
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 (http://www.openoffice.org/api/docs/common/ref/com/sun/star/awt/tree/XTreeControl.html)
На оба ваши вопроса даю (чисто умозрительно) положительные ответы. Но теперь вы ведущий, а мы наблюдатели.
Думаю, что путь выбран правильно.
Цитата: iyugov от 6 мая 2021, 22:08Пока непонятно, насколько удобно будет этим пользоваться.
Удобно. Окно диалога с деревом можно и не закрывать, если сделать его соответствующего размера (в зависимости от длины наименований).
Вам ещё один пример в помощь (прикреплен). Загружает дерево каталогов молниеносно. Выполните процедуру Main и выберите папку где-то верхнего уровня.
Upd:Обратите внимание на события мыши (mousePressed, щелчки мыши тоже определяются: см. здесь (https://forumooo.ru/index.php/topic,8656.msg58381.html#msg58381)), а также событие 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
Такой вариант ReDim - сначала экспоненциальное выделение памяти, потом линейное. В каких-то языках видел подобное - компромисс между частыми выделениями памяти и чрезмерным запасом. Да, лучше запросом получить количество сразу. Правда, если поставить сразу достаточный размер, то задержка всё равно сохранится.
Спасибо за наводки. Буду думать. Пробую несколько вариантов, в том числе с макросами на Python и, возможно, с вынесением части интерфейса в отдельный скрипт. LibreOffice в основном интересен как сервер отображения самих заданий, автоматической генерации и конвертации документов. В том, что не касается содержимого заданий, возможны варианты. К лету не жалко поэкспериментировать, но чем больше удачный выбор сэкономит времени в следующем учебном году, тем лучше.
Пока остановился всё-таки на отображении в форме, с помощью запроса с рекурсией. Надо идти дальше.
Выглядит вполне понятно, но все же не деревом.
Если же смотреть в сторону Python - контрол Treeview для tkinter выглядит лучше, поддерживает цвета, форматы, "зебру", под-деревья в другом окне.
В "LO-родном" Python - его вырезали, и через докачанный pip он не поставится. Но можно скопировать из установленного внешнего той же версии/разрядности: https://tk-tutorial.readthedocs.io/en/latest/tree/tree.html (у меня заработал даже из 3.8)
Тем оказалось уже 850, на 7 уровней вложенности. Оказалось, что такой список работает быстро, и подходящие темы в нём ищутся с меньшим числом телодвижений: вся иерархия уже раскрыта и видна, видны темы на соседних уровнях, вроде бы поддаётся фильтрации, можно вынести название из нижнего уровня вперёд и искать набором на клавиатуре. Вполне эффективный вариант получился. Кажется, он для моих целей удобнее, чем TreeView в принципе. А ещё он прямо на форме расположен, а на ней и без того есть над чем подумать, одной проблемой меньше.