Forum QGIS
Auto edycja warstwy - Wersja do druku

+- Forum QGIS (http://forum.quantum-gis.pl)
+-- Dział: Desktop GIS (http://forum.quantum-gis.pl/forum-4.html)
+--- Dział: QGIS (http://forum.quantum-gis.pl/forum-5.html)
+--- Wątek: Auto edycja warstwy (/thread-918.html)



Auto edycja warstwy - jakosek - 03-04-2013

Od jakiegoś czasu zgłębiam tajniki QGIS i większość odpowiedzi znalazłem już tu na forum lub w dokumentacji Quantuma.

Otóż:
Mam bardzo duży projekt - ponad 40 warstw do których zrobiłem personalizowane formularze edycji. W większości starałem się unikać Pythona. Przy takiej ilości danych mam ustawione otwieranie bezpośrednio formularza obiektu oraz wyszukiwanie wyników identyfikacji od góry do dołu.
Przy odpowiednim układzie warstw fajnie to działa - kliknięcie na mapie otwiera formularz pierwszego znalezionego obiektu.

I tutaj problem - warstw jest tyle że ciężko teraz odnaleźć w legendzie warstwę do której należy obiekt, do tego trzeba włączyć edycję - wybrać obiekt jeszcze raz...

Pomysł mi zaświtał żeby w funkcji inicjującej formularz umieścić fragment włączający edycję danej warstwy i po zakończeniu "OK" - zapis, "Cancel" - powrót. Znalazłem odpowiednie klasy i z konsoli Pythona potrafię coś takiego zrobić. Czy funkcja inicjująca może wogóle takie rzeczy obsługiwać? Tutaj mi się nie udało dotychczas znaleźć odpowiedzi a dokumentacja w tym kierunku jest raczej skąpa. Trochę mnie przeraża szukanie odpowiedzi w kodzie źródłowym qgisa Smile

Będę wdzięczny za podpowiedzi. Może jest inna droga?


RE: Auto edycja warstwy - borys - 03-04-2013

Nie mam pod ręką stabilnego Quantuma, tylko rozgrzebaną alfę 2.0, ale nie powinno być z tym problemu. W funkcji inicjującej włączasz tryb edycji oraz podłączasz sygnały z klawiszy OK i Cancel do dwóch kolejnych funkcji, w których implementujesz koniec trybu edycji. Z grubsza powinno to wyglądać jak w tym przykładzie:

http://nathanw.net/2011/09/05/qgis-tips-custom-feature-forms-with-python-logic/

tylko zamiast funkcji validate() tworzysz np. zaakceptowano() i odrzucono(), w których wywołujesz warstwa.commitChanges() i warstwa.rollBack(). A skąd wziąć w nich warstwę? Zwróć uwagę na zmienne globalne (tutaj nameField i myDialog) - do zmiennej globalnej np. warstwa w funkcji inicjącej zapiszesz warstwę, żeby móc ją wywołać w tych dwóch pozostałych funkcjach.

Ważna uwaga: niestety po każdej zmianie w pliku zawierającym funkcję inicjującą MUSISZ przeładować QGISa, żeby zobaczył zmianę. Dlatego próby wygodniej jest robić w konsoli.

Ważna uwaga 2: API QGIS-a 1.x i nadchodzącego wielkimi krokami 2.x się różnią, więc przeglądając dokumentację online zwracaj na to uwagę. Np. w adresie: http://www.qgis.org/api/1.8/classQgsVectorLayer.html jest człon 1.8 - bez niego wyświetli się API gałęzi master, czyli alphy 2.0.
Zasadnicza różnica przy formularzach jest taka, że w QGIS-ie 1.x do funkcji inicjującej przekazywane są id warstwy i obiektu, czyli musisz znaleźć warstwę po id. Nie pamiętam teraz, jak to najłatwiej zrobić, pewnie trzeba szukać w klasie QgsMapLayerRegistry albo QgsMapCanvas. Natomiast w QGIS-ie rozwojowym/nocnym, czyli przyszłym 2.0, do funkcji inicjującej przekazywane są już nie id warstwy i obiektu, tylko warstwa i obiekt we własnych osobach, czyli obiekty klasy QgsVectorLayer i QgsFeature. Dzięki temu można od razu wywołać warstwa.startEditing()


RE: Auto edycja warstwy - jakosek - 03-04-2013

Działa! Dziękuję bardzo za rzeczową odpowiedź!

Wszystko to gdzieś czytałem i jakoś mi to nie chciało działać. Twój post pomógł mi to usystematyzować. To że zmiany nie łapią już wyczytałem i przy takim projekcie to upierdliwe, fakt.

Obiekt typu QgsVectorLayer udało mi się wydłubać z mapCanvas().layers() według znanej nazwy Smile - działa też z legendInterface().layers()

O zmianach w funkcji inicjującej w wersji 2.0 znalazłem i uprościło by to mój kod (swoją drogą to jedna z niewielu informacji które można znaleźć na temat inicjacji formularzy poza linkiem do blogu Nathana który podałeś...)

ALE
Teraz jeszcze jedna zagadka: Qgis otwiera formularze w 2 sposoby - zależnie czy edycja jest włączona czy nie. No i mimo że wszystko fajnie, edycja włącza się tak jak zakładałem - to formularz ma wyłączone pola i przycisk "OK". Da się w tym stanie wybrać drugi formularz na tej samej warstwie i ten już jest aktywny, "OK" i "Cancel" działa prawidłowo. Tylko potem "Cancel" w pierwszym formularzu wywala QGIS Smile

Jeżeli tego nie da się przeskoczyć to marny pożytek z tej możliwości.


RE: Auto edycja warstwy - borys - 03-04-2013

Hmmm, no tak, skoro funkcja inicjująca dostaje jako pierwszy parametr okno formularza, to ono już musi być wcześniej otwarte. Takie gorące pomysły rozwiązania, pewnie wyjdą przy nich kolejne problemy:

1. Pierwszy jest partyzancki. Można spróbować na wszystkich widgetach w oknie wywołać metodę setEnabled(True), a potem uczynić okno modalnym, żeby uniemożliwić otwarcie drugiego:
Kod:
for widzet in dialog.findChildren(QLineEdit): widzet.setEnabled(True)
for widzet in dialog.findChildren(QComboBox): widzet.setEnabled(True)
itd.
dialog.setModal(True)
Zamiast kolejno wywoływać klasy widgetów można niby zastosować dialog.children(), ale to zwróci tylko najwyższy poziom, czyli np. QGridLayout i dopiero w nim, albo jeszcze głębiej, trzeba by szukać docelowych widgetów). Genralnie, nie wiem, czy to wszystko zadziała, ale wygląda obiecująco

2. Można spróbować w metodzie inicjującej zamknąć ten dialog, włączyć tryb edycji i spróbować go jakoś wywołać ponownie. Nie jestem pewien, czy gdziekolwiek w API jest wystawiona taka możliwość. Jeśli jest, to powinno to być rozwiązanie prostsze i czystsze od poprzedniego. Jeśli nie ma, to w zasadzie niemożliwe.

3. Najelegantsze ale i najbardziej pracochłonne: dać sobie spokój ze standardowym narzędziem do odpytywania i napisać wtyczkę, która tworzy własne narzędzie (potomka QgsMapTool), a wtedy można już robić co dusza zapragnie.


RE: Auto edycja warstwy - jakosek - 04-04-2013

Niestety, powalczyłem i odpuściłem.
1. sposób działa o tyle że QGIS chowa przycisk "OK" a zdefiniowanie swojego przycisku do slotu accepted() nie zapisuje wyników edycji.
2. openFeatureForm zastosowany w funkcji inicjującej zawiesza całego Qgisa.
3. Może w przyszłym projekcie, już planuję naukę pisania własnych wtyczek. Podstawy Pythona już opanowałem. Jeszcze raz dziekuję za pomoc. Fajnie że ktoś z Polski jest w ekipie rozwojowej QGIS - to wielkie wsparcie, bo dokumentacja w wielu przypadkach jest bardzo skąpa.


RE: Auto edycja warstwy - borys - 04-04-2013

Dzięki :-)

Co do pkt 1) to może ten klawisz jest tylko schowany. Nie pamiętam, czy to jest QPushButton, czy QToolButton:
Kod:
for widzet in dialog.findChildren(QPushButton): widzet.show()
for widzet in dialog.findChildren(QToolButton): widzet.show()



RE: Auto edycja warstwy - jakosek - 04-04-2013

Poszukałem dziś jeszcze trochę i znalazłem trochę informacji, tak jak by ktoś potrzebował.

Za rysowanie okna formularza odpowiada klasa QgsAttributeDialog i w niej zawarte są całe sztuczki.

Kod:
buttonBox->setStandardButtons( QDialogButtonBox::Cancel );

W ten sposób są chowane przyciski buttonBox'a. Załatwiłem to szybko i wszystko widać:
Kod:
buttonbox.setStandardButtons(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

Ale zapis zmienionych atrybutów nadal nie działa - chyba nie jest odpowiednio przekazywany spowrotem sygnał accept()...

Próbowałem nawet przenieść do Pythona poniższy fragment:
Kod:
const QgsFields& fields = mLayer->pendingFields();
   for ( int idx = 0; idx < fields.count(); ++idx )
   {
     QVariant value;

     if ( QgsAttributeEditor::retrieveValue( mProxyWidgets.value( idx ), mLayer, idx, value ) )
       mFeature->setAttribute( idx, value );
   }

Ale sobie odpuściłem, nie do końca wiem jak obsłużyć to w Pythonie (o ile wogóle się da). Problemem tu jest przekazanie konkretnego QWidget do QgsAttributeEditor.retrieveValue() bo resztę mniej - więcej kojarzę.

Dlatego temat odpuszczam, szkoda życia. Prędzej by było skompilować QGIS z drobnymi zmianami Smile


RE: Auto edycja warstwy - borys - 05-04-2013

Ja się właśnie zbieram do Valmiery [1] (straszyli łotewskimi drogami, to ruszamy pięć dni wcześniej l-) ) więc już też nie spojrzę, w którym momencie to utyka. Jeśli dotarłeś do granic możliwości tego dostosowywania formularzy, to pozostaje wtyka albo właśnie przeróbka w kodzie. Można by ją wprowadzić na stałe, tylko chyba ten przypadek nie jest zbyt typowy...

[1] http://hub.qgis.org/wiki/quantum-gis/9_QGIS_Developer_Meeting_in_Valmiera_2013


RE: Auto edycja warstwy - jakosek - 05-04-2013

Cytat:Można by ją wprowadzić na stałe, tylko chyba ten przypadek nie jest zbyt typowy...

Właściwie to w starszych wersjach QGIS było tylko okno edycji, potem to rozdzielono. Generalnie ma to sens, tylko mi w tym jednym przypadku nie pasuje.

[1] Niezła zakładka 5 dni Big Grin

UPDATE:

Udało mi się osiągnąć efekt zupełnie inną drogą. Efekty są nawet lepsze od oczekiwanych. Dodałem do wszystkich warstw akcję otwierającą formularz edycyjny _PO_ włączeniu edycji. Jak komuś brakowało możliwości otwierania formularza z akcji to może sobie coś wyłowić z tego kodu Smile (chyba nawet ktoś postulował o dodanie tego do domyślnych akcji).

Reszta bajeru która powoduje że korzysta się z tego przyjemnie to wtyczka "Hotlink" - klikając na mapie wyświetla listę dostępnych akcji na wszystkich widocznych warstwach.

Akcja w Pythonie:
Kod:
layers = qgis.utils.iface.mapCanvas().layers()
for layer in layers:
    layerType = layer.type()
    if layerType == QgsMapLayer.VectorLayer:
        layerId = layer.id()
        if layerId == 'budynki20130301104122791': //tutaj podałem ręcznie id dla danej warstwy, bo przez "Hotlink" nie można pobrać [% $layerid %]
            warstwa = layer
warstwa.startEditing()
pv = warstwa.dataProvider()
feat = QgsFeature()
while pv.nextFeature(feat):
    feat.id() == [% $id %]
qgis.utils.iface.openFeatureForm(warstwa, feat)
warstwa.commitChanges()