Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: unkier от Март 11, 2010, 17:10



Название: как удобно работать с адресами в виде дерева
Отправлено: unkier от Март 11, 2010, 17:10
имеем таблицу с адресами

id город улица дом квартира

надо представить в дереве вида

город1
 ->улица0
 ->улица1
   ->дом1
     ->кв1
 ->улица2

 ->улица2
город 2

читаю пример Simple Tree Model Example но пока чёто глухо.
как это запихать вроде понятно.
наверно с помощью qsqlquery вытащить все города, потом для каждого города вытащить все улицы, потом для каждой улицы вытащить все дома, потом для каждого дома вытащить все квартиры. при этом каждый раз создавая TreeItem и соблюдая кто чей наследник.

но есть неясности. если данные в моей таблице меняются, как и кто это отслеживает ?
как понять на какой узел дерева тыкнул юзер ? как можно как то прикрепить свои данные к TreeItem (он же вообще не от кого не наследуется) и как до него добраться когда юзер его выделил ?


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: unkier от Март 11, 2010, 17:27
или проще просто с TreeWidget напрямую без моделей и видов ?

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


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: asvil от Март 11, 2010, 18:44
необходим crosstab, pivot table или сводная таблица для sql запроса:
select страна, город, улица from страны join города on страны.ид = города.страна_ид join улицы on улицы.город_ид = города.ид
я лично по запросам crosstab, pivot table увидеть в инете реализацию на qt не смог.
вообще для этого случая нужна упрощенная сводная таблица, без сводимых значений. Так или иначе цепляю свою реализацию. Работает в proxy стиле.


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: Zmeishe от Март 11, 2010, 19:17
Адресный реестр, в общем случае, это не дерево.
Всё зависит от задачи.

Деревом можно оформить справочник населённых пунктов, если нет необходимости хранить историю смены подчинённости. Например, Сокольский район Ивановской губернии был переведён в состав Нижегородской губернии, причём, совсем недавно в 90-е годы.

Населённые пункты и улицы могут менять названия. Историю смены названий в дерево не засунешь, а вот поиск провести обязан. Т.к. пользователь не обязан знать какое название было вчера, а какое стало сегодня.
С домами тоже непросто. Есть угловые дома и многих бесит адрес ул. Ванеева 17/47
47 это что? Корпус?
У такого дома должен быть двойной адрес ул.Ванеева 17 / ул. Генкиной 47
В дерево не засунешь, а поиском найти обязан по любому адресу.
Сначала определись с архитектурой базы данных, потом ГУЙ рисуй.



Название: Re: как удобно работать с адресами в виде дерева
Отправлено: unkier от Март 11, 2010, 20:45
необходим crosstab, pivot table или сводная таблица для sql запроса:
select страна, город, улица from страны join города on страны.ид = города.страна_ид join улицы on улицы.город_ид = города.ид
а QSqlRelationalTableModel этих проблем не решает ?
я по началу хотел сделать всё по человечески с несколькими таблицами и связями. но от этого усложнения вапще непонятно как это делать.

а по поводу архитектуры. это не я так придумал. всё еще будет переделываться много раз.


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: npkitsul от Март 11, 2010, 22:10
 ???

1. База данных SQL доступная например через класс SQLRelationaTableModel самое всеобъемлеющее решение.

2. А самая компактная структура данных для самых быстрых алгоритмов будет:
Дерево для стран, областей, городов, улиц, домов, корпусов, строений.

Физический дом будет представлен листком одного из домов, корпусов, строений, т.е.
if ((p->ChildrenStart == 0) && (p->ChildrenEnd == 0))
{
      это физический дом.
}
 
Указатели на детей естественно в "indexed array", что бы поиск дома 342 был мгновенен:
house[342].

Указатели на страны, области, города, и улицы отсортированы в алфавитном порядке. Для быстрого поиска.

А для быстрого поиска по имени улицы, можно прикрепить глобальную hash table для ссылок на все улицы.



Населённые пункты и улицы могут менять названия. Историю смены названий в дерево не засунешь, а вот поиск провести обязан. Т.к. пользователь не обязан знать какое название было вчера, а какое стало сегодня.
Так вот в глобальную hash table и засунуть можно весь List исторических имён.

С домами тоже непросто. Есть угловые дома и многих бесит адрес ул. Ванеева 17/47
47 это что? Корпус?
У такого дома должен быть двойной адрес ул.Ванеева 17 / ул. Генкиной 47
Так можно сделать у дома double list синонимов, и внести их тоже в hash table.

Как оно? Это перфексионизм?

 ::)


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: break от Март 12, 2010, 01:07
А если так:

Города
Почтовые адреса
Дома
Квартиры

Все разные таблицы со своими ID

а потом делаем многие ко многим

ID города, ID Почтового адреса, ID Дома, ID Квартиры

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

и написав Хранимую процедуру или Вьев (что предпочтительнее - зависит от БД)  можно вывести в одной таблице (хотя дома с двойными адресами лучше обработать в этой процедуре чтобы вывелись одной строкой)

также через ID можно организовать отдельную таблицу с деревом



Название: Re: как удобно работать с адресами в виде дерева
Отправлено: mcrads от Март 12, 2010, 21:33
А если так?
class City: public QObject
{
    QList<Street> streetList;
    ...
}

class Street: public QObject
{
    QList<House> houseList;
......
}

и т.д.? и использовать метаобъектную информацию. например objectName(), setObjectName(), findChildren(), parent()
я таким способом реализовывал виртуальную файловую систему классами VSystem, VDir, VFile. в принципе шустренько и поиск работает удобно =)

PS Ну и естественно правильно определять родителей! тогда все будет очень просто и иерархично =)


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: viktor.kz от Март 15, 2010, 10:43
А если так (способ корявый, но работает):
2 таблицы.
Таблица типов адресов. name_ab - сокращение.
Код
SQL
CREATE TABLE address_type
   (id      number(10,0) NOT NULL,
    name    varchar2(250) NOT NULL,
    name_ab varchar2(250))
/

Для примера заполняем.
INSERT INTO ADDRESS_TYPE VALUES (null,'Город','г.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Район','р-он.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Проспект','пр.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Микрорайон','мкр.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Квартал','кв-л.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Улица','ул.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Дом','д.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Участок','уч.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Переулок','пер.');
INSERT INTO ADDRESS_TYPE VALUES (null,'Учетный квартал','уч. кв-л.');

Таблица адресов:
Код
SQL
CREATE TABLE address
  (id           number(10,0) NOT NULL,
   address_type number(10,0) NOT NULL,
   parent       number(10,0) NOT NULL,
   name         varchar2(250) NOT NULL)
/
 
parent - ID родителя. Если 0 (NULL, если not null убрать), то нет родителя (самый верхний элемент).

Вначале заполняешь города, затем улицы, дома, квартиры.
Потом делаешь всё в QTreeWidget.
Делаешь что-то типа
void BuildTree(QTreeWidgetItem *twiItem, int iParent)
twiItem - родитель в QTreeWidget. iParent - ID родителя в базе.
При старте делаешь:
BuildTree(treeWidget->invisibleRootItem(), 0);
Прогружается список верхних итемов. Когда прогружаешь, проверяешь есть ли у него дети. Если есть то добавляешь какой нибудь пустой элемент чтобы он |+| добавил.
Затем на событие void on_treeWidget_itemExpanded(QTreeWidgetItem* twiItem) вызываешь BuildTree.
Соотвественно итемы подгружаются по нажатию на |+|. Не забываешь удалять пустой итем.

Чтобы получить полный адрес в цикле пробегаешься по таблицу ADDRESS. Читаешь сокращения из ADDRESS_TYPE.
И в итоге получается: "г. Караганда ул. Киевская 121 кв. 39"

По тупому, но работает...


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: mcrads от Март 15, 2010, 22:50
жестоко... Sql для такой простенькой задачи - все равно что белазом дрова на дачу привозить блин... нужно сервер, нужно клиент, очень и очень много мороки...


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: break от Март 16, 2010, 00:48
Цитировать
жестоко... Sql для такой простенькой задачи - все равно что белазом дрова на дачу привозить блин... нужно сервер, нужно клиент, очень и очень много мороки...

При чем тут SQL - разложить данные по таблицам и составить соответствующие таблицы со связями один-ко-многим, многие-ко-многим это проблема? По мне так наследование QObject для своих классов "улица", "город" является БЕЛАЗОМ.

Задачка типовая для реляционных БД. Ключ решения - сами данные не должны лежать ни в каком дереве. Данные это ДАННЫЕ. Просто списки "Почтовых адресов", "Городов", "Стран" имеющие уникальные ID (для упрощения БД - ID могут быть сквозными по Базе), а связи устанавливаются в отдельных таблицах "Адресный рубрикатор-дерево", "Адреса-список для поиска" и т.д.


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: mcrads от Март 16, 2010, 22:23
но ведь спрашивалась обработка в виде дерева? один из выходов - QAbstractItemModel + QTreeView. второй - структуризация классами.ну тут решать тому кто спрашивал.. вариантов то много... ИМХО, таблицы выйдут немного сложнее для реализации. ИМХО и только ИМХО.


Название: Re: как удобно работать с адресами в виде дерева
Отправлено: break от Март 17, 2010, 12:05
Цитировать
таблицы выйдут немного сложнее для реализации
возможно но мне показалось что речь шла о данных которые надо где-то хранить - а если так то в случае с классами вы будете их сериализовать / десириализовать при загрузке - сохранении - Это проще?

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


ROW_ID   !   OBJECT_ID   ! PARENT__ROW_ID

PARENT_ROW_ID ссылается на ROW_ID как вторичный ключ
OBJECT_ID ссылается на ID объекта в таблице объектов, потом пишется рекурсивная хранимая процедура которая может выбрать все дерева начиная с ID ветки. С ее помощью строится модель.