Ejemplo n.º 1
0
    def displayPopupMenu(self, pos):
        if self.selectedDimStyle is None:
            return

        popupMenu = QMenu(self)
        action = QAction(QadMsg.translate("DimStyle_Dialog", "Set current"),
                         popupMenu)
        popupMenu.addAction(action)
        action.triggered.connect(self.setCurrentStyle)

        action = QAction(QadMsg.translate("DimStyle_Dialog", "Rename"),
                         popupMenu)
        popupMenu.addAction(action)
        action.triggered.connect(self.startEditingItem)

        action = QAction(
            QadMsg.translate("DimStyle_Dialog", "Modify description"),
            popupMenu)
        popupMenu.addAction(action)
        action.triggered.connect(self.updDescrSelectedDimStyle)

        action = QAction(QadMsg.translate("DimStyle_Dialog", "Remove"),
                         popupMenu)
        currDimStyleName = QadVariables.get(
            QadMsg.translate("Environment variables", "DIMSTYLE"))
        if self.selectedDimStyle.name == currDimStyleName:
            action.setDisabled(True)
        popupMenu.addAction(action)
        action.triggered.connect(self.delSelectedDimStyle)

        popupMenu.popup(self.dimStyleList.mapToGlobal(pos))
Ejemplo n.º 2
0
class ViewSTRWidget(WIDGET, BASE):
    """
    Search and browse the social tenure relationship
    of all participating entities.
    """
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self.btnSearch.setIcon(GuiUtils.get_icon('search.png'))
        self.btnClearSearch.setIcon(GuiUtils.get_icon('reset.png'))

        self._plugin = plugin

        self.search_done = False
        # self.tbPropertyPreview.set_iface(self._plugin.iface)
        QTimer.singleShot(
            100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))

        self.curr_profile = current_profile()

        self.spatial_units = self.curr_profile.social_tenure.spatial_units
        # Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin)
        self.geom_cols = []
        for spatial_unit in self.spatial_units:
            each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit)
            self.geom_cols.extend(each_geom_col)

        # Configure notification bar
        self._notif_search_config = NotificationBar(self.vl_notification)

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        # Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""

        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True)

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc, self.str_doc_model,
            self)

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved)

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
        self.add_spatial_unit_layer()
        self.details_tree_view = DetailsTreeView(iface, self._plugin, self)
        # else:
        #     self.details_tree_view = self._plugin.details_tree_view
        self.details_tree_view.activate_feature_details(True)
        self.details_tree_view.add_tree_view()
        self.details_tree_view.model.clear()

        count = pg_table_count(self.curr_profile.social_tenure.name)
        self.setWindowTitle(
            self.tr('{}{}'.format(self.windowTitle(),
                                  '- ' + str(count) + ' rows')))

        self.active_spu_id = -1

        self.toolBox.setStyleSheet('''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            ''')

        self.details_tree_view.view.setStyleSheet('''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            ''')

    def add_tool_buttons(self):
        """
        Add toolbar buttons of add, edit and delete buttons.
        :return: None
        :rtype: NoneType
        """
        tool_buttons = QToolBar()
        tool_buttons.setObjectName('form_toolbar')
        tool_buttons.setIconSize(QSize(16, 16))

        self.addSTR = QAction(GuiUtils.get_icon('add.png'),
                              QApplication.translate('ViewSTRWidget', 'Add'),
                              self)

        self.editSTR = QAction(GuiUtils.get_icon('edit.png'),
                               QApplication.translate('ViewSTRWidget', 'Edit'),
                               self)

        self.deleteSTR = QAction(
            GuiUtils.get_icon('remove.png'),
            QApplication.translate('ViewSTRWidget', 'Remove'), self)

        tool_buttons.addAction(self.addSTR)
        tool_buttons.addAction(self.editSTR)
        tool_buttons.addAction(self.deleteSTR)

        self.toolbarVBox.addWidget(tool_buttons)

    def initGui(self):
        """
        Initialize widget
        """
        self.tb_actions.setVisible(False)
        self._load_entity_configurations()

        self.add_tool_buttons()

        # Connect signals
        self.tbSTREntity.currentChanged.connect(self.entityTabIndexChanged)
        self.btnSearch.clicked.connect(self.searchEntityRelations)
        self.btnClearSearch.clicked.connect(self.clearSearch)
        # self.tvSTRResults.expanded.connect(self.onTreeViewItemExpanded)

        # Set the results treeview to accept requests for context menus
        # self.tvSTRResults.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.tvSTRResults.customContextMenuRequested.connect(
        #     self.onResultsContextMenuRequested
        # )

        if not self._can_create:
            self.addSTR.hide()

        if not self._can_edit:
            self.editSTR.hide()
        else:
            self.editSTR.setDisabled(True)
        if not self._can_delete:
            self.deleteSTR.hide()
        else:
            self.deleteSTR.setDisabled(True)

        self.addSTR.triggered.connect(self.load_new_str_editor)

        self.deleteSTR.triggered.connect(self.delete_str)

        self.editSTR.triggered.connect(self.load_edit_str_editor)

        # Load async for the current widget
        self.entityTabIndexChanged(0)

    def init_progress_dialog(self):
        """
        Initializes the progress dialog.
        """
        self.progress = QProgressBar(self)
        self.progress.resize(self.width(), 10)
        self.progress.setTextVisible(False)

    def add_spatial_unit_layer(self):
        """
        Add the spatial unit layer into the map canvas for later use.
        """
        # Used for startup of view STR, just add the first geom layer.
        if len(self.geom_cols) > 0:
            for spatial_unit in self.spatial_units:
                layer_name_item = self.sp_unit_manager.geom_col_layer_name(
                    spatial_unit.name, self.geom_cols[0])
                self.sp_unit_manager.add_layer_by_name(layer_name_item)

    def _check_permissions(self):
        """
        Enable/disable actions based on the
        permissions defined in the content
        group.
        """
        if self._can_edit:
            self.tb_actions.addAction(self._new_str_action)
        else:
            self.tb_actions.removeAction(self._new_str_action)

        if len(self.tb_actions.actions()) == 0:
            self.tb_actions.setVisible(False)

        else:
            self.tb_actions.setVisible(True)

    def _load_entity_configurations(self):
        """
        Specify the entity configurations.
        """
        try:
            self.parties = self.curr_profile.social_tenure.parties
            tb_str_entities = self.parties + self.spatial_units

            for i, t in enumerate(tb_str_entities):
                QApplication.processEvents()
                entity_cfg = self._entity_config_from_profile(
                    str(t.name), t.short_name)

                if not entity_cfg is None:
                    entity_widget = self.add_entity_config(entity_cfg)

                    # entity_widget.setNodeFormatter(
                    #     EntityNodeFormatter(
                    #         entity_cfg, self.tvSTRResults, self
                    #     )
                    # )

        except DummyException as pe:
            self._notif_search_config.clear()
            self._notif_search_config.insertErrorNotification(str(pe))

    def _entity_config_from_profile(self, table_name, short_name):
        """
        Creates an EntityConfig object from the table name.
        :param table_name: Name of the database table.
        :type table_name: str
        :return: Entity configuration object.
        :rtype: EntityConfig
        """
        table_display_name = format_name(short_name)

        entity = self.curr_profile.entity_by_name(table_name)
        model = entity_model(entity)

        if model is not None:
            # Entity configuration
            entity_cfg = EntityConfiguration()
            entity_cfg.Title = table_display_name
            entity_cfg.STRModel = model
            entity_cfg.data_source_name = table_name
            for col, factory in self._get_widget_factory(entity):
                entity_cfg.LookupFormatters[col.name] = factory

            # Load filter and display columns
            # using only those which are of
            # numeric/varchar type
            searchable_columns = entity_searchable_columns(entity)
            display_columns = entity_display_columns(entity)
            for c in searchable_columns:
                if c != 'id':
                    entity_cfg.filterColumns[c] = format_name(c)
            for c in display_columns:
                if c != 'id':
                    entity_cfg.displayColumns[c] = format_name(c)
            return entity_cfg
        else:
            return None

    def _get_widget_factory(self, entity):
        """
        Get widget factory for specific column type
        :param entity: Current column entity object
        :type entity: Entity
        :return c: Column object corresponding to the widget factory
        :rtype c: BaseColumn
        :return col_factory: Widget factory corresponding to the column type
        :rtype col_factory: ColumnWidgetRegistry
        """
        for c in entity.columns.values():
            col_factory = ColumnWidgetRegistry.factory(c.TYPE_INFO)
            if col_factory is not None:
                yield c, col_factory(c)

    def add_entity_config(self, config):
        """
        Set an entity configuration option and
        add it to the 'Search Entity' tab.
        """
        entityWidg = STRViewEntityWidget(config)
        entityWidg.asyncStarted.connect(self._progressStart)
        entityWidg.asyncFinished.connect(self._progressFinish)

        tabIndex = self.tbSTREntity.addTab(entityWidg, config.Title)

        return entityWidg

    def entityTabIndexChanged(self, index):
        """
        Raised when the tab index of the entity search tab widget changes.
        """
        # Get the current widget in the tab container
        entityWidget = self.tbSTREntity.currentWidget()

        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.loadAsync()

    def searchEntityRelations(self):
        """
        Slot that searches for matching items for
        the specified entity and corresponding STR entities.
        """

        entityWidget = self.tbSTREntity.currentWidget()

        entity_name = entityWidget.config.data_source_name

        self._reset_controls()

        if isinstance(entityWidget, EntitySearchItem):
            valid, msg = entityWidget.validate()

            if not valid:
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(msg)

                return

            results, searchWord = entityWidget.executeSearch()

            # Show error message
            if len(results) == 0:
                noResultsMsg = QApplication.translate(
                    'ViewSTR', 'No results found for "{}"'.format(searchWord))
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(noResultsMsg)

                return

            party_names = [
                e.name for e in self.curr_profile.social_tenure.parties
            ]
            entity = self.curr_profile.entity_by_name(entity_name)

            result_ids = [r.id for r in results]

            if entity_name in party_names:

                self.active_spu_id = self.details_tree_view.search_party(
                    entity, result_ids)
            else:
                self.details_tree_view.search_spatial_unit(entity, result_ids)

            # self.tbPropertyPreview._iface.activeLayer().selectByExpression("id={}".format(self.active_spu_id))
            # self.details_tree_view._selected_features = self.tbPropertyPreview._iface.activeLayer().selectedFeatures()
            # self._load_root_node(entity_name, formattedNode)

    def clearSearch(self):
        """
        Clear search input parameters (for current widget) and results.
        """
        entityWidget = self.tbSTREntity.currentWidget()
        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.reset()
        self._reset_controls()

    def _reset_controls(self):
        # Clear tree view
        self._resetTreeView()

        # Clear document listings
        self._deleteSourceDocTabs()

        # Remove spatial unit memory layer
        self.tbPropertyPreview.remove_layer()

    def on_select_results(self):
        """
        Slot which is raised when the selection
        is changed in the tree view
        selection model.
        """
        if len(self.details_tree_view.view.selectedIndexes()) < 1:
            self.disable_buttons()
            return
        self.search_done = True

        index = self.details_tree_view.view.selectedIndexes()[0]
        item = self.details_tree_view.model.itemFromIndex(index)

        QApplication.processEvents()

        # STR node - edit social tenure relationship
        if item.text() == self.details_tree_view.str_text:
            entity = self.curr_profile.social_tenure
            str_model = self.details_tree_view.str_models[item.data()]

            self.details_tree_view.selected_model = str_model
            self.details_tree_view.selected_item = SelectedItem(item)

            documents = self.details_tree_view._supporting_doc_models(
                entity.name, str_model)

            self._load_source_documents(documents)
            # if there is supporting document,
            # expand supporting document tab
            if len(documents) > 0:
                self.toolBox.setCurrentIndex(1)
            self.disable_buttons(False)

        # party node - edit party
        elif item.data() in self.details_tree_view.spatial_unit_items.keys():
            self.toolBox.setCurrentIndex(0)
            entity = self.details_tree_view.spatial_unit_items[item.data()]

            model = self.details_tree_view.feature_model(entity, item.data())
            self.draw_spatial_unit(entity.name, model)
            self.disable_buttons()

            canvas = iface.mapCanvas()
            if canvas:
                canvas.zoomToFullExtent()

        else:
            self.disable_buttons()

    def disable_buttons(self, status=True):
        if self._can_edit:
            self.deleteSTR.setDisabled(status)
        if self._can_delete:
            self.editSTR.setDisabled(status)

    def str_party_column_obj(self, record):
        """
        Gets the current party column name in STR
        table by finding party column with value
        other than None.
        :param record: The STR record or result.
        :type record: Dictionary
        :return: The party column name with value.
        :rtype: String
        """
        for party in self.parties:
            party_name = party.short_name.lower()
            party_id = '{}_id'.format(party_name)
            if party_id not in record.__dict__:
                return None
            if record.__dict__[party_id] != None:
                party_id_obj = getattr(self.str_model, party_id)
                return party_id_obj

    def load_edit_str_editor(self):
        self.details_tree_view.edit_selected_node(self.details_tree_view)
        self.btnSearch.click()
        self.disable_buttons()

    def load_new_str_editor(self):
        try:
            # Check type of node and perform corresponding action
            add_str = STREditor()
            add_str.exec_()

        except DummyException as ex:
            QMessageBox.critical(
                self._plugin.iface.mainWindow(),
                QApplication.translate("STDMPlugin", "Loading Error"), str(ex))

    def delete_str(self):
        self.details_tree_view.delete_selected_item()
        self.btnSearch.click()
        self.disable_buttons()

    def onSourceDocumentRemoved(self, container_id, doc_uuid, removed_doc):
        """
        Slot raised when a source document is removed from the container.
        If there are no documents in the specified container then remove
        the tab.
        """
        curr_container = self.tbSupportingDocs.currentWidget()
        curr_doc_widget = curr_container.findChildren(DocumentWidget)

        for doc in curr_doc_widget:
            if doc.fileUUID == doc_uuid:
                doc.deleteLater()
        self.removed_docs = removed_doc

    def draw_spatial_unit(self, entity_name, model):
        """
        Render the geometry of the given spatial unit in the spatial view.
        :param row_id: Sqlalchemy object representing a feature.
        """
        entity = self.curr_profile.entity_by_name(entity_name)

        self.tbPropertyPreview.draw_spatial_unit(entity, model)

    def showEvent(self, event):
        """
        (Re)load map layers in the viewer and main canvas.
        :param event: Window event
        :type event: QShowEvent
        """
        self.setEnabled(True)
        if QTimer is not None:
            QTimer.singleShot(200, self.init_mirror_map)

        return QMainWindow.showEvent(self, event)

    def init_mirror_map(self):
        self._notify_no_base_layers()
        # Add spatial unit layer if it doesn't exist
        self.tbPropertyPreview.refresh_canvas_layers()
        self.tbPropertyPreview.load_web_map()

    def _notify_no_base_layers(self):
        """
        Checks if there are any base layers that will be used when
        visualizing the spatial units. If there are no base layers
        then insert warning message.
        """
        self._notif_search_config.clear()

        num_layers = len(QgsProject.instance().mapLayers())
        if num_layers == 0:
            msg = QApplication.translate(
                "ViewSTR", "No basemap layers are loaded in the "
                "current project. Basemap layers "
                "enhance the visualization of spatial units.")
            self._notif_search_config.insertWarningNotification(msg)

    def _deleteSourceDocTabs(self):
        """
        Removes all source document tabs and deletes their references.
        """
        tabCount = self.tbSupportingDocs.count()

        while tabCount != 0:
            srcDocWidget = self.tbSupportingDocs.widget(tabCount - 1)
            self.tbSupportingDocs.removeTab(tabCount - 1)
            del srcDocWidget
            tabCount -= 1

        self._strID = None
        self._source_doc_manager.reset()

    def _resetTreeView(self):
        """
        Clears the results tree view.
        """
        # Reset tree view
        strModel = self.details_tree_view.view.model()
        resultsSelModel = self.details_tree_view.view.selectionModel()

        if strModel:
            strModel.clear()

        if resultsSelModel:
            if self.search_done:
                resultsSelModel.selectionChanged.disconnect(
                    self.on_select_results)
            resultsSelModel.selectionChanged.connect(self.on_select_results)

    def _load_source_documents(self, source_docs):
        """
        Load source documents into document listing widget.
        """
        # Configure progress dialog
        progress_msg = QApplication.translate(
            "ViewSTR", "Loading supporting documents...")

        progress_dialog = QProgressDialog(self)
        if len(source_docs) > 0:
            progress_dialog.setWindowTitle(progress_msg)
            progress_dialog.setRange(0, len(source_docs))
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setFixedWidth(380)
            progress_dialog.show()
            progress_dialog.setValue(0)
        self._notif_search_config.clear()

        self.tbSupportingDocs.clear()
        self._source_doc_manager.reset()

        if len(source_docs) < 1:
            empty_msg = QApplication.translate(
                'ViewSTR', 'No supporting document is uploaded '
                'for this social tenure relationship.')
            self._notif_search_config.clear()
            self._notif_search_config.insertWarningNotification(empty_msg)

        for i, (doc_type_id, doc_obj) in enumerate(source_docs.items()):

            # add tabs, and container and widget for each tab
            tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id]

            tab_widget = QWidget()
            tab_widget.setObjectName(tab_title)

            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName('widget_layout_' + tab_title)

            scrollArea = QScrollArea(tab_widget)
            scrollArea.setFrameShape(QFrame.NoFrame)

            scrollArea_contents = QWidget()
            scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title)

            tab_layout = QVBoxLayout(scrollArea_contents)
            tab_layout.setObjectName('layout_' + tab_title)

            scrollArea.setWidgetResizable(True)

            scrollArea.setWidget(scrollArea_contents)
            cont_layout.addWidget(scrollArea)

            self._source_doc_manager.registerContainer(tab_layout, doc_type_id)

            for doc in doc_obj:

                try:
                    # add doc widgets
                    self._source_doc_manager.insertDocFromModel(
                        doc, doc_type_id)
                except DummyException as ex:
                    LOGGER.debug(str(ex))

            self.tbSupportingDocs.addTab(tab_widget, tab_title)
            progress_dialog.setValue(i + 1)

    # def _on_node_reference_changed(self, rootHash):
    #     """
    #     Method for resetting document listing and map preview
    #     if another root node and its children
    #     are selected then the documents are reset as
    #     well as the map preview control.
    #     """
    #     if rootHash != self._curr_rootnode_hash:
    #         self._deleteSourceDocTabs()
    #         self._curr_rootnode_hash = rootHash

    def _progressStart(self):
        """
        Load progress dialog window.
        For items whose durations is unknown,
        'isindefinite' = True by default.
        If 'isindefinite' is False, then
        'rangeitems' has to be specified.
        """
        pass

    def _progressFinish(self):
        """
        Hide progress dialog window.
        """
        pass

    def _edit_permissions(self):
        """
        Returns True/False whether the current logged in user
        has permissions to create new social tenure relationships.
        If true, then the system assumes that
        they can also edit STR records.
        """
        canEdit = False
        userName = globals.APP_DBCONN.User.UserName
        authorizer = Authorizer(userName)
        newSTRCode = "9576A88D-C434-40A6-A318-F830216CA15A"

        # Get the name of the content from the code
        cnt = Content()
        createSTRCnt = cnt.queryObject().filter(
            Content.code == newSTRCode).first()
        if createSTRCnt:
            name = createSTRCnt.name
            canEdit = authorizer.CheckAccess(name)

        return canEdit