def __init__(self, parent=None):
        """Constructor."""
        super(BDTopoImporterDialog, self).__init__(parent)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        # load db connections
        self.dbselect = QgsProviderConnectionComboBox('postgres', self)
        self.horizontalLayout_4.addWidget(self.dbselect)
        self.schemaselect = QgsDatabaseSchemaComboBox('postgres', self.dbselect.currentConnection(), self)
        self.dbselect.connectionChanged.connect(self.schemaselect.setConnectionName)
        self.horizontalLayout_5.addWidget(self.schemaselect)

        # load layer list
        data_path = os.path.join(os.path.dirname(__file__), 'data.json')
        with open(data_path, 'r') as f:
            json_data = json.load(f)
            layer_data = json_data['themes']
        model = QStandardItemModel()
        root = model.invisibleRootItem()
        for theme in layer_data:
            item = QStandardItem(theme['title'])
            item.setData(theme['pathname'])
            root.appendRow(item)
            for layer in theme['layers']:
                layeritem = QStandardItem(layer['name'])
                layeritem.setCheckable(True)
                # layeritem.setCheckState(Qt.Checked)
                layeritem.setData(layer['filename'])
                item.appendRow(layeritem)
        
        self.treeView_layers.setModel(model)
        self.treeView_layers.expandAll()

        # extract parameter dialog
        self.settings_dlg = ExtractorSettingsDialog(self)

        # file access buttons
        self.pushButton_folder_select.clicked.connect(self.onFolderSelectClicked)
        self.pushButton_file_select.clicked.connect(self.onFileSelectClicked)

        # check/uncheck all buttons
        self.pushButton_checkall.clicked.connect(self.onCheckAllClicked)
        self.pushButton_uncheckall.clicked.connect(self.onUncheckAllClicked)

        # settings button
        self.toolButton_extract_parameters.clicked.connect(self.onExtractParametersClicked)
Beispiel #2
0
class ConfigDialog(BASE, WIDGET):
    def __init__(self):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(
            self.style().standardPixmap(QStyle.SP_DirClosedIcon), QIcon.Normal,
            QIcon.Off)
        self.groupIcon.addPixmap(
            self.style().standardPixmap(QStyle.SP_DirOpenIcon), QIcon.Normal,
            QIcon.On)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(self.tr('Search...'))

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        self.searchBox.textChanged.connect(self.textChanged)

        self.fillTree()

        self.saveMenus = False
        self.tree.expanded.connect(self.itemExpanded)

    def textChanged(self):
        text = str(self.searchBox.text().lower())
        self._filterItem(self.model.invisibleRootItem(), text)
        if text:
            self.tree.expandAll()
        else:
            self.tree.collapseAll()

    def _filterItem(self, item, text):
        if item.hasChildren():
            show = False
            for i in range(item.rowCount()):
                child = item.child(i)
                showChild = self._filterItem(child, text)
                show = (showChild or show)
            self.tree.setRowHidden(item.row(), item.index().parent(), not show)
            return show

        elif isinstance(item, QStandardItem):
            hide = bool(text) and (text not in item.text().lower())
            self.tree.setRowHidden(item.row(), item.index().parent(), hide)
            return not hide

    def fillTree(self):
        self.fillTreeUsingProviders()

    def fillTreeUsingProviders(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels(
            [self.tr('Setting'), self.tr('Value')])

        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()
        """
        Filter 'General', 'Models' and 'Scripts' items
        """
        priorityKeys = [
            self.tr('General'),
            self.tr('Models'),
            self.tr('Scripts')
        ]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            rootItem.insertRow(0, [groupItem, emptyItem])
            # add menu item only if it has any search matches
            for setting in settings[group]:
                if setting.hidden or setting.name.startswith("MENU_"):
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])
        """
        Filter 'Providers' items
        """
        providersItem = QStandardItem(self.tr('Providers'))
        icon = QgsApplication.getThemeIcon("/processingAlgorithm.svg")
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in list(settings.keys()):
            if group in priorityKeys or group == menusSettingsGroup:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for setting in settings[group]:
                if setting.hidden:
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])
        """
        Filter 'Menus' items
        """
        self.menusItem = QStandardItem(self.tr('Menus'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
        self.menusItem.setIcon(icon)
        self.menusItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [self.menusItem, emptyItem])

        button = QPushButton(self.tr('Reset to defaults'))
        button.clicked.connect(self.resetMenusToDefaults)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(button)
        layout.addStretch()
        widget = QWidget()
        widget.setLayout(layout)
        self.tree.setIndexWidget(emptyItem.index(), widget)

        for provider in QgsApplication.processingRegistry().providers():
            providerDescription = provider.name()
            groupItem = QStandardItem(providerDescription)
            icon = provider.icon()
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for alg in provider.algorithms():
                algItem = QStandardItem(alg.displayName())
                algItem.setIcon(icon)
                algItem.setEditable(False)
                try:
                    settingMenu = ProcessingConfig.settings["MENU_" + alg.id()]
                    settingButton = ProcessingConfig.settings["BUTTON_" +
                                                              alg.id()]
                    settingIcon = ProcessingConfig.settings["ICON_" + alg.id()]
                except:
                    continue
                self.items[settingMenu] = SettingItem(settingMenu)
                self.items[settingButton] = SettingItem(settingButton)
                self.items[settingIcon] = SettingItem(settingIcon)
                menuLabelItem = QStandardItem("Menu path")
                menuLabelItem.setEditable(False)
                buttonLabelItem = QStandardItem("Add button in toolbar")
                buttonLabelItem.setEditable(False)
                iconLabelItem = QStandardItem("Icon")
                iconLabelItem.setEditable(False)
                emptyItem = QStandardItem()
                emptyItem.setEditable(False)
                algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]])
                algItem.insertRow(0,
                                  [buttonLabelItem, self.items[settingButton]])
                algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]])
                groupItem.insertRow(0, [algItem, emptyItem])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            self.menusItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def resetMenusToDefaults(self):
        for provider in QgsApplication.processingRegistry().providers():
            for alg in provider.algorithms():
                d = defaultMenuEntries.get(alg.id(), "")
                setting = ProcessingConfig.settings["MENU_" + alg.id()]
                item = self.items[setting]
                item.setData(d, Qt.EditRole)
        self.saveMenus = True

    def accept(self):
        qsettings = QgsSettings()
        for setting in list(self.items.keys()):
            if setting.group != menusSettingsGroup or self.saveMenus:
                if isinstance(setting.value, bool):
                    setting.setValue(
                        self.items[setting].checkState() == Qt.Checked)
                else:
                    try:
                        setting.setValue(str(self.items[setting].text()))
                    except ValueError as e:
                        QMessageBox.warning(
                            self, self.tr('Wrong value'),
                            self.tr('Wrong value for parameter "{0}":\n\n{1}').
                            format(setting.description, str(e)))
                        return
                setting.save(qsettings)

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        for p in QgsApplication.processingRegistry().providers():
            p.refreshAlgorithms()
        QApplication.restoreOverrideCursor()

        settingsWatcher.settingsChanged.emit()

    def itemExpanded(self, idx):
        if idx == self.menusItem.index():
            self.saveMenus = True
        self.adjustColumns()

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
Beispiel #3
0
class ConfigDialog(BASE, WIDGET):

    def __init__(self, showSearch=True):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off)
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On)

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        if showSearch:
            if hasattr(self.searchBox, 'setPlaceholderText'):
                self.searchBox.setPlaceholderText(QApplication.translate('ConfigDialog', 'Search…'))
            self.searchBox.textChanged.connect(self.textChanged)
        else:
            self.searchBox.hide()

        self.fillTree()

        self.saveMenus = False
        self.tree.expanded.connect(self.itemExpanded)
        self.auto_adjust_columns = True

    def textChanged(self, text=None):
        if text is not None:
            text = str(text.lower())
        else:
            text = str(self.searchBox.text().lower())
        found = self._filterItem(self.model.invisibleRootItem(), text)

        self.auto_adjust_columns = False
        if text:
            self.tree.expandAll()
        else:
            self.tree.collapseAll()

        self.adjustColumns()
        self.auto_adjust_columns = True

        if text:
            return found
        else:
            self.tree.collapseAll()
            return False

    def _filterItem(self, item, text, forceShow=False):
        if item.hasChildren():
            show = forceShow or isinstance(item, QStandardItem) and bool(text) and (text in item.text().lower())
            for i in range(item.rowCount()):
                child = item.child(i)
                show = self._filterItem(child, text, forceShow) or show
            self.tree.setRowHidden(item.row(), item.index().parent(), not show)
            return show

        elif isinstance(item, QStandardItem):
            show = forceShow or bool(text) and (text in item.text().lower())
            self.tree.setRowHidden(item.row(), item.index().parent(), not show)
            return show

    def fillTree(self):
        self.fillTreeUsingProviders()

    def fillTreeUsingProviders(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels([self.tr('Setting'),
                                              self.tr('Value')])

        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()

        """
        Filter 'General', 'Models' and 'Scripts' items
        """
        priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            rootItem.insertRow(0, [groupItem, emptyItem])
            if not group in settings:
                continue

            # add menu item only if it has any search matches
            for setting in settings[group]:
                if setting.hidden or setting.name.startswith("MENU_"):
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

        """
        Filter 'Providers' items
        """
        providersItem = QStandardItem(self.tr('Providers'))
        icon = QgsApplication.getThemeIcon("/processingAlgorithm.svg")
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in list(settings.keys()):
            if group in priorityKeys or group == menusSettingsGroup:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for setting in settings[group]:
                if setting.hidden:
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])

        """
        Filter 'Menus' items
        """
        self.menusItem = QStandardItem(self.tr('Menus'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
        self.menusItem.setIcon(icon)
        self.menusItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [self.menusItem, emptyItem])

        button = QPushButton(self.tr('Reset to defaults'))
        button.clicked.connect(self.resetMenusToDefaults)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(button)
        layout.addStretch()
        widget = QWidget()
        widget.setLayout(layout)
        self.tree.setIndexWidget(emptyItem.index(), widget)

        for provider in QgsApplication.processingRegistry().providers():
            providerDescription = provider.name()
            groupItem = QStandardItem(providerDescription)
            icon = provider.icon()
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for alg in provider.algorithms():
                algItem = QStandardItem(alg.displayName())
                algItem.setIcon(icon)
                algItem.setEditable(False)
                try:
                    settingMenu = ProcessingConfig.settings["MENU_" + alg.id()]
                    settingButton = ProcessingConfig.settings["BUTTON_" + alg.id()]
                    settingIcon = ProcessingConfig.settings["ICON_" + alg.id()]
                except:
                    continue
                self.items[settingMenu] = SettingItem(settingMenu)
                self.items[settingButton] = SettingItem(settingButton)
                self.items[settingIcon] = SettingItem(settingIcon)
                menuLabelItem = QStandardItem("Menu path")
                menuLabelItem.setEditable(False)
                buttonLabelItem = QStandardItem("Add button in toolbar")
                buttonLabelItem.setEditable(False)
                iconLabelItem = QStandardItem("Icon")
                iconLabelItem.setEditable(False)
                emptyItem = QStandardItem()
                emptyItem.setEditable(False)
                algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]])
                algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]])
                algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]])
                groupItem.insertRow(0, [algItem, emptyItem])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            self.menusItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def resetMenusToDefaults(self):
        for provider in QgsApplication.processingRegistry().providers():
            for alg in provider.algorithms():
                d = defaultMenuEntries.get(alg.id(), "")
                setting = ProcessingConfig.settings["MENU_" + alg.id()]
                item = self.items[setting]
                item.setData(d, Qt.EditRole)
        self.saveMenus = True

    def accept(self):
        qsettings = QgsSettings()
        for setting in list(self.items.keys()):
            if setting.group != menusSettingsGroup or self.saveMenus:
                if isinstance(setting.value, bool):
                    setting.setValue(self.items[setting].checkState() == Qt.Checked)
                else:
                    try:
                        setting.setValue(str(self.items[setting].text()))
                    except ValueError as e:
                        QMessageBox.warning(self, self.tr('Wrong value'),
                                            self.tr('Wrong value for parameter "{0}":\n\n{1}').format(setting.description, str(e)))
                        return
                setting.save(qsettings)

        with OverrideCursor(Qt.WaitCursor):
            for p in QgsApplication.processingRegistry().providers():
                p.refreshAlgorithms()

        settingsWatcher.settingsChanged.emit()

    def itemExpanded(self, idx):
        if idx == self.menusItem.index():
            self.saveMenus = True
        if self.auto_adjust_columns:
            self.adjustColumns()

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
Beispiel #4
0
class ConfigDialog(BASE, WIDGET):

    def __init__(self, toolbox):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.toolbox = toolbox
        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off)
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(self.tr('Search...'))

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        self.searchBox.textChanged.connect(self.textChanged)

        self.fillTree()

        self.tree.expanded.connect(self.adjustColumns)

    def textChanged(self):
        text = str(self.searchBox.text().lower())
        self._filterItem(self.model.invisibleRootItem(), text)
        if text:
            self.tree.expandAll()
        else:
            self.tree.collapseAll()

    def _filterItem(self, item, text):
        if item.hasChildren():
            show = False
            for i in range(item.rowCount()):
                child = item.child(i)
                showChild = self._filterItem(child, text)
                show = (showChild or show)
            self.tree.setRowHidden(item.row(), item.index().parent(), not show)
            return show

        elif isinstance(item, QStandardItem):
            hide = bool(text) and (text not in item.text().lower())
            self.tree.setRowHidden(item.row(), item.index().parent(), hide)
            return not hide

    def fillTree(self):
        self.fillTreeUsingProviders()

    def fillTreeUsingProviders(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels([self.tr('Setting'),
                                              self.tr('Value')])

        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()

        """
        Filter 'General', 'Models' and 'Scripts' items
        """
        priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            rootItem.insertRow(0, [groupItem, emptyItem])
            # add menu item only if it has any search matches
            for setting in settings[group]:
                if setting.hidden or setting.name.startswith("MENU_"):
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

        """
        Filter 'Providers' items
        """
        providersItem = QStandardItem(self.tr('Providers'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png'))
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in list(settings.keys()):
            if group in priorityKeys or group == menusSettingsGroup:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for setting in settings[group]:
                if setting.hidden:
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])

        """
        Filter 'Menus' items
        """
        menusItem = QStandardItem(self.tr('Menus'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
        menusItem.setIcon(icon)
        menusItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [menusItem, emptyItem])

        button = QPushButton(self.tr('Reset to defaults'))
        button.clicked.connect(self.resetMenusToDefaults)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(button)
        layout.addStretch()
        widget = QWidget()
        widget.setLayout(layout)
        self.tree.setIndexWidget(emptyItem.index(), widget)

        providers = Processing.providers
        for provider in providers:
            providerDescription = provider.getDescription()
            groupItem = QStandardItem(providerDescription)
            icon = provider.getIcon()
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for alg in provider.algs:
                algItem = QStandardItem(alg.i18n_name or alg.name)
                algItem.setIcon(icon)
                algItem.setEditable(False)
                try:
                    settingMenu = ProcessingConfig.settings["MENU_" + alg.commandLineName()]
                    settingButton = ProcessingConfig.settings["BUTTON_" + alg.commandLineName()]
                    settingIcon = ProcessingConfig.settings["ICON_" + alg.commandLineName()]
                except:
                    continue
                self.items[settingMenu] = SettingItem(settingMenu)
                self.items[settingButton] = SettingItem(settingButton)
                self.items[settingIcon] = SettingItem(settingIcon)
                menuLabelItem = QStandardItem("Menu path")
                menuLabelItem.setEditable(False)
                buttonLabelItem = QStandardItem("Add button in toolbar")
                buttonLabelItem.setEditable(False)
                iconLabelItem = QStandardItem("Icon")
                iconLabelItem.setEditable(False)
                emptyItem = QStandardItem()
                emptyItem.setEditable(False)
                algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]])
                algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]])
                algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]])
                groupItem.insertRow(0, [algItem, emptyItem])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            menusItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def resetMenusToDefaults(self):
        providers = Processing.providers
        for provider in providers:
            for alg in provider.algs:
                d = defaultMenuEntries.get(alg.commandLineName(), "")
                setting = ProcessingConfig.settings["MENU_" + alg.commandLineName()]
                item = self.items[setting]
                item.setData(d, Qt.EditRole)

    def accept(self):
        for setting in list(self.items.keys()):
            if isinstance(setting.value, bool):
                setting.setValue(self.items[setting].checkState() == Qt.Checked)
            else:
                try:
                    setting.setValue(str(self.items[setting].text()))
                except ValueError as e:
                    QMessageBox.warning(self, self.tr('Wrong value'),
                                        self.tr('Wrong value for parameter "%s":\n\n%s' % (setting.description, str(e))))
                    return
            setting.save()
        Processing.updateAlgsList()
        settingsWatcher.settingsChanged.emit()

        QDialog.accept(self)

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
Beispiel #5
0
class ConfigDialog(BASE, WIDGET):
    def __init__(self, toolbox):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.toolbox = toolbox
        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(
            self.style().standardPixmap(QStyle.SP_DirClosedIcon), QIcon.Normal,
            QIcon.Off)
        self.groupIcon.addPixmap(
            self.style().standardPixmap(QStyle.SP_DirOpenIcon), QIcon.Normal,
            QIcon.On)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(self.tr('Search...'))

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        self.searchBox.textChanged.connect(self.textChanged)

        self.fillTree()

        self.tree.expanded.connect(self.adjustColumns)

    def textChanged(self):
        text = unicode(self.searchBox.text().lower())
        self._filterItem(self.model.invisibleRootItem(), text)
        if text:
            self.tree.expandAll()
        else:
            self.tree.collapseAll()

    def _filterItem(self, item, text):
        if item.hasChildren():
            show = False
            for i in xrange(item.rowCount()):
                child = item.child(i)
                showChild = self._filterItem(child, text)
                show = (showChild or show)
            self.tree.setRowHidden(item.row(), item.index().parent(), not show)
            return show

        elif isinstance(item, QStandardItem):
            hide = bool(text) and (text not in item.text().lower())
            self.tree.setRowHidden(item.row(), item.index().parent(), hide)
            return not hide

    def fillTree(self):
        self.fillTreeUsingProviders()

    def fillTreeUsingProviders(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels(
            [self.tr('Setting'), self.tr('Value')])

        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()
        """
        Filter 'General', 'Models' and 'Scripts' items
        """
        priorityKeys = [
            self.tr('General'),
            self.tr('Models'),
            self.tr('Scripts')
        ]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            rootItem.insertRow(0, [groupItem, emptyItem])
            # add menu item only if it has any search matches
            for setting in settings[group]:
                if setting.hidden or setting.name.startswith("MENU_"):
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])
        """
        Filter 'Providers' items
        """
        providersItem = QStandardItem(self.tr('Providers'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png'))
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in settings.keys():
            if group in priorityKeys or group == menusSettingsGroup:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for setting in settings[group]:
                if setting.hidden:
                    continue

                labelItem = QStandardItem(setting.description)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])
        """
        Filter 'Menus' items
        """
        menusItem = QStandardItem(self.tr('Menus (requires restart)'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
        menusItem.setIcon(icon)
        menusItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)

        rootItem.insertRow(0, [menusItem, emptyItem])

        providers = Processing.providers
        for provider in providers:
            providerDescription = provider.getDescription()
            groupItem = QStandardItem(providerDescription)
            icon = provider.getIcon()
            groupItem.setIcon(icon)
            groupItem.setEditable(False)

            for alg in provider.algs:
                algItem = QStandardItem(alg.i18n_name or alg.name)
                algItem.setIcon(icon)
                algItem.setEditable(False)
                try:
                    settingMenu = ProcessingConfig.settings[
                        "MENU_" + alg.commandLineName()]
                    settingButton = ProcessingConfig.settings[
                        "BUTTON_" + alg.commandLineName()]
                    settingIcon = ProcessingConfig.settings[
                        "ICON_" + alg.commandLineName()]
                except:
                    continue
                self.items[settingMenu] = SettingItem(settingMenu)
                self.items[settingButton] = SettingItem(settingButton)
                self.items[settingIcon] = SettingItem(settingIcon)
                menuLabelItem = QStandardItem("Menu path")
                menuLabelItem.setEditable(False)
                buttonLabelItem = QStandardItem("Add button in toolbar")
                buttonLabelItem.setEditable(False)
                iconLabelItem = QStandardItem("Icon")
                iconLabelItem.setEditable(False)
                emptyItem = QStandardItem()
                emptyItem.setEditable(False)
                algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]])
                algItem.insertRow(0,
                                  [buttonLabelItem, self.items[settingButton]])
                algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]])
                groupItem.insertRow(0, [algItem, emptyItem])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)

            menusItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def accept(self):
        for setting in self.items.keys():
            if isinstance(setting.value, bool):
                setting.setValue(
                    self.items[setting].checkState() == Qt.Checked)
            else:
                try:
                    setting.setValue(unicode(self.items[setting].text()))
                except ValueError as e:
                    QMessageBox.warning(
                        self, self.tr('Wrong value'),
                        self.tr('Wrong value for parameter "%s":\n\n%s' %
                                (setting.description, unicode(e))))
                    return
            setting.save()
        Processing.updateAlgsList()
        settingsWatcher.settingsChanged.emit()
        updateMenus()

        QDialog.accept(self)

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
Beispiel #6
0
class QVDistrictesBarris(QObject):

    __distBarrisCSV = r'Dades\DIST_BARRIS.csv'
    __zones = r'Dades\Zones.gpkg'

    def __init__(self):
        super().__init__()
        self.labels = []
        self.registre = {}

        # Model
        self.model = QStandardItemModel()
        self.llegirZonesGPKG()
        #self.llegirDistrictesBarrisCSV()

        # View
        self.view = QTreeView()

        self.view.setContextMenuPolicy(Qt.ActionsContextMenu)

        self.actExpand = QAction("Expandeix/Contreu Tot", self)
        self.actExpand.setStatusTip("Expand")
        self.actExpand.triggered.connect(self.expand_all)
        self.view.addAction(self.actExpand)

        self.view.setModel(self.model)
        self.iniView()

    def expand_all(self):
        """Expandir o contraer todo el arbol, dependiendo de si detecta que esta extendido o contraido
        """
        if self.view.isExpanded(self.model.index(0, 0, QModelIndex())):
            self.view.collapseAll()
        else:
            self.view.expandAll()

    def iniView(self):
        self.view.setHeaderHidden(True)
        for i in range(self.model.columnCount()):
            if i == 0:
                self.view.setColumnHidden(i, False)
                self.view.resizeColumnToContents(i)
                self.view.resizeColumnToContents(i)
            else:
                self.view.setColumnHidden(i, True)
        self.view.setEditTriggers(QTreeView.NoEditTriggers)
        self.expand_all()

    def llegirZonesGPKG(self):
        try:
            QvFuncions.setReadOnlyFile(self.__zones)
            pathDistrictes = self.__zones + '|layername=districtes'
            layerDistrictes = QgsVectorLayer(pathDistrictes, 'ogr')
            pathBarris = self.__zones + '|layername=barris'
            layerBarris = QgsVectorLayer(pathBarris, 'ogr')

            rowsDistrictes = layerDistrictes.getFeatures()
            llistaDistrictes = []
            for rowD in rowsDistrictes:
                #print(rowD.attributes())
                #zona = ""
                num_districte = rowD.attributes()[1]
                nom_districte = rowD.attributes()[2]
                num_barri = ""

                nom_barri = ""
                geometria = rowD.geometry().boundingBox()
                x_min = str(geometria.xMinimum())
                y_min = str(geometria.yMinimum())
                x_max = str(geometria.xMaximum())
                y_max = str(geometria.yMaximum())
                item = [
                    num_districte, nom_districte, num_barri, nom_barri, x_min,
                    y_min, x_max, y_max
                ]
                llistaDistrictes.append(item)

            def ordenaPerNumDistricte(elem):
                return elem[0]

            llistaDistrictes.sort(key=ordenaPerNumDistricte)
            #print(llistaDistrictes)

            rowsBarris = layerBarris.getFeatures()
            llistaBarris = []
            for rowB in rowsBarris:
                #print(rowB.attributes())
                #zona = ""
                num_districte = rowB.attributes()[3]
                nom_districte = llistaDistrictes[int(num_districte) - 1][1]
                num_barri = rowB.attributes()[1]
                nom_barri = rowB.attributes()[2]
                geometria = rowB.geometry().boundingBox()
                x_min = str(geometria.xMinimum())
                y_min = str(geometria.yMinimum())
                x_max = str(geometria.xMaximum())
                y_max = str(geometria.yMaximum())
                item = [
                    num_districte, nom_districte, num_barri, nom_barri, x_min,
                    y_min, x_max, y_max
                ]
                llistaBarris.append(item)

            def ordenaPerNumBarri(elem):
                return elem[2]

            llistaBarris.sort(key=ordenaPerNumBarri)
            #print(llistaBarris)

            self.labels = [
                "ZONA", "DISTRICTE", "NOM_DISTRICTE", "BARRI", "NOM_BARRI",
                "X_MIN", "Y_MIN", "X_MAX", "Y_MAX"
            ]
            root = self.model.invisibleRootItem()
            self.model.setColumnCount(len(self.labels))
            self.model.setHorizontalHeaderLabels(self.labels)

            #Afegir Barcelona com a arrel de l'arbre
            bcn_dades = [
                "00", "Barcelona", "00", "Barcelona", "419710.0553820258",
                "4573818.80776309", "436533.35", "4591775.02"
            ]
            bcn = [QStandardItem("Barcelona")]
            for item in bcn_dades:
                bcn.append(QStandardItem(item))
            root.appendRow(bcn)

            ultimaDistr = -1
            itDist = 0
            for b in llistaBarris:
                if ultimaDistr != int(b[0]):  #Afegir següent districte
                    dist = [QStandardItem(llistaDistrictes[itDist][1])]
                    for i in range(0, len(llistaDistrictes[itDist])):
                        dist.append(QStandardItem(llistaDistrictes[itDist][i]))
                    bcn[0].appendRow(dist)

                    itDist = itDist + 1
                    #Afegir següent Barri
                barri = [QStandardItem(b[3])]
                for item in b:
                    barri.append(QStandardItem(item))
                dist[0].appendRow(barri)
                ultimaDistr = int(b[0])
            return True
        except:
            print("Error en construcció de l'arbre de zones")
            return False

    # def llegirDistrictesBarrisCSV(self):
    #     try:
    #         first = True
    #         with open(self.__distBarrisCSV, newline='') as csvFile:
    #             reader = csv.DictReader(csvFile, delimiter=';')
    #             root = self.model.invisibleRootItem()
    #             for row in reader:
    #                 if first: # Primer registro
    #                     self.labels = ['ZONA']
    #                     for item in row:
    #                         self.labels.append(item)
    #                     self.model.setColumnCount(len(self.labels))
    #                     self.model.setHorizontalHeaderLabels(self.labels)
    #                     first = False
    #                 if row['BARRI'] == '': # Registro de distrito
    #                     dist = [QStandardItem(row['NOM_DISTRICTE'])]
    #                     for item in row.values():
    #                         dist.append(QStandardItem(item))
    #                     root.appendRow(dist)
    #                 else: # Registro de barrio
    #                     barri = [QStandardItem(row['NOM_BARRI'])]
    #                     for item in row.values():
    #                         barri.append(QStandardItem(item))
    #                     dist[0].appendRow(barri)
    #         return True
    #     except:
    #         print('QDistrictesBarris.llegirDistrictesBarrisCSV(): ', sys.exc_info()[0], sys.exc_info()[1])
    #         return False

    def llegirRegistre(self):
        try:
            click = self.view.currentIndex()
            #Controlarem si s'ha canviat d'índex o no
            if hasattr(self, 'ultimIndex') and self.ultimIndex == click:
                return self.registre
            self.ultimIndex = click
            self.registre = {}
            for i in range(self.model.columnCount()):
                index = click.sibling(click.row(), i)
                item = self.model.itemFromIndex(index)
                self.registre[self.labels[i]] = item.text()
            self.registre['RANG'] = QgsRectangle(float(self.registre['X_MIN']), \
                                                float(self.registre['Y_MIN']), \
                                                float(self.registre['X_MAX']), \
                                                float(self.registre['Y_MAX']))
        except:
            print('QDistrictesBarris.llegirRegistre(): ',
                  sys.exc_info()[0],
                  sys.exc_info()[1])
        finally:
            return self.registre

    def llegirRang(self):
        return self.llegirRegistre()['RANG']

    def esDistricte(self):
        return self.llegirRegistre()['BARRI'] == ''

    def esBarri(self):
        return not self.esDistricte()

    def llegirNom(self):
        if self.esDistricte():
            distr = self.llegirRegistre()["NOM_DISTRICTE"]
            distr_d = distr + "_d"
            return distr_d
        else:
            barri = self.llegirRegistre()["NOM_BARRI"]
            if barri == "Barcelona":
                return barri
            else:
                barri_b = barri + "_b"
                return barri_b

    def llegirID(self):
        if self.esDistricte():
            return self.llegirRegistre()['DISTRICTE']
        return self.llegirRegistre()['BARRI']
Beispiel #7
0
class QRAVEMetaWidget(QDockWidget, Ui_QRAVEMetaWidgetBase):
    def __init__(self, parent=None):
        """Constructor."""
        super(QRAVEMetaWidget, self).__init__(parent)
        self.setupUi(self)

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(self.open_menu)
        self.treeView.doubleClicked.connect(self.default_tree_action)

        self.settings = Settings()
        self.model = QStandardItemModel()
        self.treeView.setModel(self.model)
        self.meta = None
        self.menu = QMenu()

        # Initialize our classes
        self.hide()

    @pyqtSlot(str, str, dict, bool)
    def load(self, label: str, meta_type: str, meta: dict, show: bool = False):
        # re-initialize our model
        self.model.clear()
        self.meta = meta
        root_item = self.model.invisibleRootItem()
        self.model.setColumnCount(2)
        self.model.setHorizontalHeaderLabels(['Meta Name', 'Meta Value'])

        if meta_type == MetaType.PROJECT:
            self.treeView.setHeaderHidden(False)
            self.setWindowTitle('Project MetaData: {}'.format(label))
            self.treeView.setEnabled(True)
            if meta is not None and len(meta.keys()) > 0:
                if 'project' in meta and len(meta['project'].keys()) > 0:
                    proj_meta = QStandardItem('Project Meta')
                    proj_meta_font = proj_meta.font()
                    proj_meta_font.setBold(True)
                    proj_meta.setFont(proj_meta_font)
                    for k, v in meta['project'].items():
                        proj_meta.appendRow(
                            [QStandardItem(k),
                             QStandardItem(v)])
                    root_item.appendRow(proj_meta)
                if 'warehouse' in meta and len(meta['warehouse'].keys()) > 0:
                    wh_meta = QStandardItem('Warehouse Meta')
                    wh_meta_font = proj_meta.font()
                    wh_meta_font.setBold(True)
                    wh_meta.setFont(wh_meta_font)
                    for k, v in meta['warehouse'].items():
                        wh_meta.appendRow([QStandardItem(k), QStandardItem(v)])
                    root_item.appendRow(wh_meta)

        elif meta_type == MetaType.FOLDER:
            self.setWindowTitle('Folder: {}'.format(label))
            self.treeView.setHeaderHidden(True)
            self.treeView.setEnabled(False)
            self.model.setColumnCount(1)
            self.model.setHorizontalHeaderLabels(['Meta Name'])
            no_item = QStandardItem('Folders have no MetaData')
            no_item.setTextAlignment(Qt.AlignCenter)
            no_f = no_item.font()
            no_f.setItalic(True)
            no_item.setFont(no_f)
            root_item.appendRow(no_item)

        elif meta_type == MetaType.LAYER:
            self.setWindowTitle('Layer MetaData: {}'.format(label))
            self.treeView.setEnabled(True)
            self.treeView.setHeaderHidden(False)
            if meta is not None and len(meta.keys()) > 0:
                for k, v in meta.items():
                    root_item.appendRow([QStandardItem(k), QStandardItem(v)])
            else:
                self.treeView.setHeaderHidden(True)
                self.treeView.setEnabled(False)
                self.model.setColumnCount(1)
                self.model.setHorizontalHeaderLabels(['Meta Name'])
                no_item = QStandardItem('This layer has no MetaData')
                no_item.setTextAlignment(Qt.AlignCenter)
                no_f = no_item.font()
                no_f.setItalic(True)
                no_item.setFont(no_f)
                root_item.appendRow(no_item)
        elif meta_type == MetaType.NONE:
            self.treeView.setHeaderHidden(True)
            self.treeView.setEnabled(False)
            self.model.setColumnCount(1)
            self.setWindowTitle('Riverscapes MetaData: {}'.format(label))
            no_item = QStandardItem('This item cannot have metadata')
            no_item.setTextAlignment(Qt.AlignCenter)
            no_f = no_item.font()
            no_f.setItalic(True)
            no_item.setFont(no_f)
            root_item.appendRow(no_item)
            return

        # self.tree.header().setDefaultSectionSize(180)

        # self._populateTree(self.tree, )
        # Finally expand all levels
        self.treeView.expandAll()
        if show is True:
            self.show()

    def closeEvent(self, event):
        self.hide()

    def default_tree_action(self, index):
        item = self.model.itemFromIndex(index)
        data = item.data(Qt.UserRole)

    def open_menu(self, position):

        indexes = self.treeView.selectedIndexes()
        if len(indexes) < 1 or self.meta is None or len(self.meta.keys()) == 0:
            return

        # No multiselect so there is only ever one item
        item_name = self.model.itemFromIndex(indexes[0])
        item_val = self.model.itemFromIndex(
            indexes[1]) if len(indexes) > 0 else None

        self.menu.clear()
        if item_val is not None:
            row_text = {item_name.text(): item_val.text()}
            self.menu.addAction('Copy Name to Clipboard',
                                lambda: self.copy(item_name.text()))
            self.menu.addAction('Copy Value to Clipboard',
                                lambda: self.copy(item_val.text()))
            self.menu.addAction(
                'Copy Row to Clipboard (json)', lambda: self.copy(
                    json.dumps(row_text, indent=4, sort_keys=True)))
        self.menu.addAction(
            'Copy All to Clipboard (json)',
            lambda: self.copy(json.dumps(self.meta, indent=4, sort_keys=True)))

        self.menu.exec_(self.treeView.viewport().mapToGlobal(position))

    def copy(self, data: str):
        cb = QGuiApplication.clipboard()
        cb.clear(mode=cb.Clipboard)
        cb.setText(data, mode=cb.Clipboard)
Beispiel #8
0
class QRiSDockWidget(QtWidgets.QDockWidget, FORM_CLASS):

    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        """Constructor."""
        super(QRiSDockWidget, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://doc.qt.io/qt-5/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        self.settings = Settings()

        self.qris_project = None
        self.menu = ContextMenu()

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.treeView.customContextMenuRequested.connect(self.open_menu)
        # self.treeView.doubleClicked.connect(self.default_tree_action)
        # self.treeView.clicked.connect(self.item_change)
        # self.treeView.expanded.connect(self.expand_tree_item)

        self.model = QStandardItemModel()
        self.treeView.setModel(self.model)

    # Take this out of init so that nodes can be added as new data is added and imported;
    def build_tree_view(self, qris_project, new_item=None):
        """Builds items in the tree view based on dictionary values that are part of the project"""
        self.qris_project = qris_project

        self.model.clear()
        self.tree_state = {}
        rootNode = self.model.invisibleRootItem()

        # set the project root
        project_node = QStandardItem(self.qris_project.project_name)
        project_node.setIcon(QIcon(':/plugins/qris_toolbar/icon.png'))
        project_node.setData('project_root', item_code['item_type'])
        rootNode.appendRow(project_node)
        self.treeView.setExpanded(project_node.index(), True)

        # Add project extent layers to tree
        extent_folder = QStandardItem("Project Extents")
        extent_folder.setIcon(QIcon(':/plugins/qris_toolbar/test_folder.png'))
        extent_folder.setData('extent_folder', item_code['item_type'])
        project_node.appendRow(extent_folder)

        for extent in self.qris_project.project_extents.values():
            extent_node = QStandardItem(extent.display_name)
            extent_node.setIcon(
                QIcon(':/plugins/qris_toolbar/test_project_extent.png'))
            extent_node.setData('extent_node', item_code['item_type'])
            extent_node.setData(extent, item_code['INSTANCE'])
            extent_folder.appendRow(extent_node)

        # Add project layers node
        layers_folder = QStandardItem("Project Layers")
        layers_folder.setIcon(QIcon(':/plugins/qris_toolbar/test_folder.png'))
        layers_folder.setData('layers_folder', item_code['item_type'])
        project_node.appendRow(layers_folder)

        # TODO extend this for geometry types and raster layers
        for layer in self.qris_project.project_vector_layers.values():
            layer_node = QStandardItem(layer.display_name)
            # TODO change icon by type
            layer_node.setIcon(QIcon(':/plugins/qris_toolbar/test_layers.png'))
            layer_node.setData('layer_node', item_code['item_type'])
            layer_node.setData(layer, item_code['INSTANCE'])
            layers_folder.appendRow(layer_node)

        # # Add riverscape surfaces node
        # # TODO go through and add layers to the tree
        # riverscape_surfaces_node = QStandardItem("Riverscape Surfaces")
        # riverscape_surfaces_node.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        # riverscape_surfaces_node.setData('riverscape_surfaces_folder', item_code['item_type'])
        # riverscape_surfaces_node.setData('group', item_code['item_layer'])
        # project_node.appendRow(riverscape_surfaces_node)

        # # Add riverscape segments node
        # # TODO go through and add layers to the tree
        # riverscape_segments_node = QStandardItem("Riverscape Segments")
        # riverscape_segments_node.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        # riverscape_segments_node.setData('riverscape_segments_folder', item_code['item_type'])
        # riverscape_segments_node.setData('group', item_code['item_layer'])
        # project_node.appendRow(riverscape_segments_node)

        # # Add detrended rasters to tree
        # detrended_rasters = QStandardItem("Detrended Rasters")
        # detrended_rasters.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        # detrended_rasters.setData("DetrendedRastersFolder", item_code['item_type'])
        # detrended_rasters.setData('group', item_code['item_layer'])
        # project_node.appendRow(detrended_rasters)

        # for raster in self.qris_project.detrended_rasters.values():
        #     detrended_raster = QStandardItem(raster.name)
        #     detrended_raster.setIcon(QIcon(':/plugins/qris_toolbar/qris_raster.png'))
        #     detrended_raster.setData('DetrendedRaster', item_code['item_type'])
        #     detrended_raster.setData(raster, item_code['INSTANCE'])
        #     detrended_raster.setData('raster_layer', item_code['item_layer'])
        #     detrended_rasters.appendRow(detrended_raster)

        #     if len(raster.surfaces.values()) > 0:
        #         item_surfaces = QStandardItem("Surfaces")
        #         item_surfaces.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        #         item_surfaces.setData('group', item_code['item_layer'])
        #         detrended_raster.appendRow(item_surfaces)
        #         for surface in raster.surfaces.values():
        #             item_surface = QStandardItem(surface.name)
        #             item_surface.setIcon(QIcon(':/plugins/qris_toolbar/layers/Polygon.png'))
        #             item_surface.setData('DetrendedRasterSurface', item_code['item_type'])
        #             item_surface.setData('surface_layer', item_code['item_layer'])
        #             item_surface.setData(surface, item_code['INSTANCE'])
        #             item_surfaces.appendRow(item_surface)

        # # Add assessments to tree
        # assessments_parent_node = QStandardItem("Riverscape Assessments")
        # assessments_parent_node.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        # assessments_parent_node.setData('assessments_folder', item_code['item_type'])
        # assessments_parent_node.setData('group', item_code['item_layer'])
        # project_node.appendRow(assessments_parent_node)

        # if self.qris_project.project_assessments:
        #     self.qris_project.assessments_path = os.path.join(self.qris_project.project_path, "Assessments.gpkg")
        #     assessments_layer = QgsVectorLayer(self.qris_project.assessments_path + "|layername=assessments", "assessments", "ogr")
        #     for assessment_feature in assessments_layer.getFeatures():
        #         assessment_node = QStandardItem(assessment_feature.attribute('assessment_date').toString('yyyy-MM-dd'))
        #         assessment_node.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        #         assessment_node.setData('dam_assessment', item_code['item_type'])
        #         assessment_node.setData('group', item_code['item_layer'])
        #         assessment_node.setData(assessment_feature.attribute('fid'), item_code['feature_id'])
        #         assessments_parent_node.appendRow(assessment_node)

        # assessments_parent_node.sortChildren(Qt.AscendingOrder)

        # Add designs to tree
        design_folder = QStandardItem("Low-Tech Designs")
        design_folder.setIcon(QIcon(':/plugins/qris_toolbar/test_folder.png'))
        design_folder.setData('design_folder', item_code['item_type'])
        project_node.appendRow(design_folder)
        self.treeView.setExpanded(design_folder.index(), True)

        design_geopackage_path = self.qris_project.project_designs.geopackage_path(
            self.qris_project.project_path)
        designs_path = design_geopackage_path + '|layername=designs'
        if os.path.exists(design_geopackage_path):
            designs_layer = QgsVectorLayer(designs_path, "designs", "ogr")
            for design_feature in designs_layer.getFeatures():
                # If these data types stick this should be refactored into a create node function
                design_node = QStandardItem(design_feature.attribute('name'))
                design_node.setIcon(
                    QIcon(':/plugins/qris_toolbar/test_design.png'))
                design_node.setData('design', item_code['item_type'])
                design_node.setData(design_feature.attribute('fid'),
                                    item_code['feature_id'])
                design_folder.appendRow(design_node)

                # TODO add the structure, footprint, and zoi to the tree under each design

            # TODO This just doesn't work very well
            design_folder.sortChildren(Qt.AscendingOrder)

        # populate structure types
        structure_type_folder = QStandardItem("Structure Types")
        structure_type_folder.setIcon(
            QIcon(':/plugins/qris_toolbar/test_settings.png'))
        structure_type_folder.setData('structure_type_folder',
                                      item_code['item_type'])
        design_folder.appendRow(structure_type_folder)

        structure_type_path = design_geopackage_path + '|layername=structure_types'
        structure_type_layer = QgsVectorLayer(structure_type_path,
                                              "structure_types", "ogr")
        for structure_type in structure_type_layer.getFeatures():
            structure_type_node = QStandardItem(
                structure_type.attribute('name'))
            # TODO change the icon
            structure_type_node.setIcon(
                QIcon(':/plugins/qris_toolbar/test_structure.png'))
            structure_type_node.setData('structure_type',
                                        item_code['item_type'])
            structure_type_node.setData(structure_type.attribute('fid'),
                                        item_code['feature_id'])
            structure_type_folder.appendRow(structure_type_node)

        # populate design phases types
        phase_folder = QStandardItem("Implementation Phases")
        # TODO change icon
        phase_folder.setIcon(QIcon(':/plugins/qris_toolbar/test_settings.png'))
        phase_folder.setData('phase_folder', item_code['item_type'])
        design_folder.appendRow(phase_folder)

        phase_path = design_geopackage_path + '|layername=phases'
        phase_layer = QgsVectorLayer(phase_path, "phases", "ogr")
        for phase in phase_layer.getFeatures():
            phase_node = QStandardItem(phase.attribute('name'))
            # TODO change the icon
            phase_node.setIcon(QIcon(':/plugins/qris_toolbar/test_phase.png'))
            phase_node.setData('phase', item_code['item_type'])
            phase_node.setData(phase.attribute('fid'), item_code['feature_id'])
            phase_folder.appendRow(phase_node)

        # populate zoi types
        zoi_type_folder = QStandardItem("ZOI Types")
        zoi_type_folder.setIcon(
            QIcon(':/plugins/qris_toolbar/test_settings.png'))
        zoi_type_folder.setData('zoi_type_folder', item_code['item_type'])
        design_folder.appendRow(zoi_type_folder)

        zoi_type_path = design_geopackage_path + '|layername=zoi_types'
        zoi_type_layer = QgsVectorLayer(zoi_type_path, "zoi_types", "ogr")
        for zoi_type in zoi_type_layer.getFeatures():
            zoi_type_node = QStandardItem(zoi_type.attribute('name'))
            # TODO change the icon
            zoi_type_node.setIcon(
                QIcon(':/plugins/qris_toolbar/test_influence.png'))
            zoi_type_node.setData('zoi_type', item_code['item_type'])
            zoi_type_node.setData(zoi_type.attribute('fid'),
                                  item_code['feature_id'])
            zoi_type_folder.appendRow(zoi_type_node)

        # Add a placed for photos
        # photos_folder = QStandardItem("Project Photos")
        # photos_folder.setIcon(QIcon(':/plugins/qris_toolbar/BrowseFolder.png'))
        # photos_folder.setData('photos_folder', item_code['item_type'])
        # project_node.appendRow(photos_folder)

        # TODO for now we are expanding the map however need to remember expanded state or add new nodes as we add data
        # self.treeView.expandAll()

        # Check if new item is in the tree, if it is pass it to the add_to_map function
        # Adds a test comment
        if new_item is not None and new_item != '':
            selected_item = self._find_item_in_model(new_item)
            if selected_item is not None:
                add_to_map(self.qris_project, self.model, selected_item)

    def _find_item_in_model(self, name):
        """Looks in the tree for an item name passed from the dataChange method."""
        # TODO may want to pass this is a try except block and give an informative error message
        selected_item = self.model.findItems(name, Qt.MatchRecursive)[0]
        return selected_item

    def get_item_expanded_state(self):
        """Recursively records a list of the expanded state for items in the tree"""

    def closeEvent(self, event):
        self.qris_project = None
        self.closingPlugin.emit()
        event.accept()

    def open_menu(self, position):
        """Connects signals as context menus to items in the tree"""
        self.menu.clear()
        indexes = self.treeView.selectedIndexes()
        if len(indexes) < 1:
            return

        # No multiselect so there is only ever one item
        idx = indexes[0]
        if not idx.isValid():
            return

        model_item = self.model.itemFromIndex(indexes[0])
        item_type = model_item.data(item_code['item_type'])

        if item_type == 'project_root':
            self.menu.addAction('EXPAND_ALL', lambda: self.expand_tree())
            self.menu.addAction('COLLAPSE_ALL', lambda: self.collapse_tree())
            self.menu.addAction(
                'REFRESH_TREE',
                lambda: self.build_tree_view(self.qris_project, None))
        elif item_type == "extent_folder":
            self.menu.addAction('ADD_PROJECT_EXTENT_LAYER',
                                lambda: self.import_project_extent_layer())
            self.menu.addAction('CREATE_BLANK_PROJECT_EXTENT_LAYER',
                                lambda: self.create_blank_project_extent())
        elif item_type == "layers_folder":
            self.menu.addAction('IMPORT_PROJECT_LAYER',
                                lambda: self.import_project_layer())
        elif item_type == "layer_node":
            self.menu.addAction(
                'ADD_TO_MAP',
                lambda: add_to_map(self.qris_project, self.model, model_item))
        elif item_type in ['extent_node', 'Project_Extent']:
            # self.menu.addAction('UPDATE_PROJECT_EXTENT', lambda: self.update_project_extent(model_item))
            # self.menu.addAction('DELETE_PROJECT_EXTENT', lambda: self.delete_project_extent(model_item))
            self.menu.addAction(
                'ADD_TO_MAP',
                lambda: add_to_map(self.qris_project, self.model, model_item))
        elif item_type == "design_folder":
            self.menu.addAction('ADD_DESIGN', lambda: self.add_design())
        elif item_type == "design":
            self.menu.addAction(
                'ADD_TO_MAP_OR_UPDATE_SYMBOLOGY',
                lambda: add_to_map(self.qris_project, self.model, model_item))
        elif item_type == "structure_type_folder":
            self.menu.addAction('ADD_STRUCTURE_TYPE',
                                lambda: self.add_structure_type())
        elif item_type == "zoi_type_folder":
            self.menu.addAction('ADD_ZOI_TYPE', lambda: self.add_zoi_type())
        elif item_type == "phase_folder":
            self.menu.addAction('ADD_PHASE', lambda: self.add_phase())
        else:
            self.menu.clear()
        self.menu.exec_(self.treeView.viewport().mapToGlobal(position))

    def expand_tree(self):
        self.treeView.expandAll()
        return

    def collapse_tree(self):
        self.treeView.collapseAll()
        return

    def add_assessment(self):
        """Initiates adding a new assessment"""
        self.assessment_dialog = AssessmentDlg(self.qris_project)
        self.assessment_dialog.dateEdit_assessment_date.setDate(
            QDate.currentDate())
        self.assessment_dialog.dataChange.connect(self.build_tree_view)
        self.assessment_dialog.show()

    def add_design(self):
        """Initiates adding a new design"""
        self.design_dialog = DesignDlg(self.qris_project)
        # TODO remove this stuff about date
        self.design_dialog.dataChange.connect(self.build_tree_view)
        self.design_dialog.show()

    def add_structure_type(self):
        """Initiates adding a structure type and the structure type dialog"""
        # TODO First check if the path to the database exists
        design_geopackage_path = self.qris_project.project_designs.geopackage_path(
            self.qris_project.project_path)
        if os.path.exists(design_geopackage_path):
            self.structure_type_dialog = StructureTypeDlg(self.qris_project)
            self.structure_type_dialog.dataChange.connect(self.build_tree_view)
            self.structure_type_dialog.show()
        else:
            # TODO move the creation of the design data model so that this isn't necessary
            QMessageBox.information(
                self, "Structure Types",
                "Please create a new project design before adding structure types"
            )

    def add_zoi_type(self):
        """Initiates adding a zoi type and the zoi type dialog"""
        # TODO First check if the path to the database exists
        design_geopackage_path = self.qris_project.project_designs.geopackage_path(
            self.qris_project.project_path)
        if os.path.exists(design_geopackage_path):
            self.zoi_type_dialog = ZoiTypeDlg(self.qris_project)
            self.zoi_type_dialog.dataChange.connect(self.build_tree_view)
            self.zoi_type_dialog.show()
        else:
            # TODO move the creation of the design data model so that this isn't necessary
            QMessageBox.information(
                self, "Structure Types",
                "Please create a new project design before adding a new influence type"
            )

    def add_phase(self):
        """Initiates adding a new phase within the phase dialog"""
        # TODO First check if the path to the database exists
        design_geopackage_path = self.qris_project.project_designs.geopackage_path(
            self.qris_project.project_path)
        if os.path.exists(design_geopackage_path):
            self.phase_dialog = PhaseDlg(self.qris_project)
            self.phase_dialog.dataChange.connect(self.build_tree_view)
            self.phase_dialog.show()
        else:
            # TODO move the creation of the design data model so that this isn't necessary
            QMessageBox.information(
                self, "Structure Types",
                "Please create a new project design before adding phases")

    # This will kick off importing photos
    def import_photos(self):
        pass

    def add_detrended_raster(self):
        # last_browse_path = self.settings.getValue('lastBrowsePath')
        # last_dir = os.path.dirname(last_browse_path) if last_browse_path is not None else None
        dialog_return = QFileDialog.getOpenFileName(
            None, "Add Detrended Raster to QRiS project", None,
            self.tr("Raster Data Sources (*.tif)"))
        if dialog_return is not None and dialog_return[
                0] != "" and os.path.isfile(dialog_return[0]):
            self.addDetrendedDlg = AddDetrendedRasterDlg(
                None, dialog_return[0], self.qris_project)
            self.addDetrendedDlg.dataChange.connect(self.build_tree_view)
            self.addDetrendedDlg.exec()

    def import_project_extent_layer(self):
        """launches the dialog that supports import of a project extent layer polygon"""
        select_layer = QgsDataSourceSelectDialog()
        select_layer.exec()
        uri = select_layer.uri()
        if uri is not None and uri.isValid() and uri.wkbType == 3:
            self.project_extent_dialog = ProjectExtentDlg(
                uri, self.qris_project)
            self.project_extent_dialog.dataChange.connect(self.build_tree_view)
            self.project_extent_dialog.exec_()
        else:
            QMessageBox.critical(self, "Invalid Layer",
                                 "Please select a valid polygon layer")

    def create_blank_project_extent(self):
        """Adds a blank project extent that will be edited by the user"""
        self.project_extent_dialog = ProjectExtentDlg(None, self.qris_project)
        self.project_extent_dialog.dataChange.connect(self.build_tree_view)
        self.project_extent_dialog.exec_()

    def update_project_extent(self):
        """Renames the project extent layer"""
        pass

    # def delete_project_extent(self, selected_item):
    #     """Deletes a project extent layer"""
    #     display_name = selected_item.data(item_code['INSTANCE']).display_name
    #     feature_name = selected_item.data(item_code['INSTANCE']).feature_name
    #     geopackage_path = selected_item.data(item_code['INSTANCE']).geopackage_path(self.qris_project.project_path)

    #     delete_ok = QMessageBox.question(self, f"Delete extent", f"Are you f*****g sure you wanna delete the extent layer: {display_name}")
    #     if delete_ok == QMessageBox.Yes:
    #         # remove from the map if it's there
    #         # TODO consider doing this based on the path
    #         for layer in QgsProject.instance().mapLayers().values():
    #             if layer.name() == display_name:
    #                 QgsProject.instance().removeMapLayers([layer.id()])
    #                 iface.mapCanvas().refresh()

    #         # TODO be sure to test whether the table exists first
    #         gdal_delete = gdal.OpenEx(geopackage_path, gdal.OF_UPDATE, allowed_drivers=['GPKG'])
    #         error = gdal_delete.DeleteLayer(feature_name)
    #         gdal_delete.ExecuteSQL('VACUUM')
    #         # TODO remove this from the Extents dictionary that will also remove from promect xml
    #         del(self.qris_project.project_extents[feature_name])
    #         # refresh the project xml
    #         self.qris_project.write_project_xml()
    #         # refresh the tree
    #         self.build_tree_view(self.qris_project, None)
    #     else:
    #         QMessageBox.information(self, "Delete extent", "No layers were deleted")

    def import_project_layer(self):
        """launches a dialog that supports import of project layers that can be clipped to a project extent"""
        select_layer = QgsDataSourceSelectDialog()
        select_layer.exec()
        uri = select_layer.uri()
        if uri is not None and uri.isValid():  # and uri.wkbType == 3:
            self.project_layer_dialog = ProjectLayerDlg(uri, self.qris_project)
            self.project_layer_dialog.dataChange.connect(self.build_tree_view)
            self.project_layer_dialog.exec_()
        else:
            QMessageBox.critical(self, "Invalid Layer",
                                 "Please select a valid gis layer")

    def explore_elevations(self, selected_item):
        raster = selected_item.data(item_code['INSTANCE'])
        self.elevation_widget = ElevationDockWidget(raster, self.qris_project)
        self.settings.iface.addDockWidget(Qt.LeftDockWidgetArea,
                                          self.elevation_widget)
        self.elevation_widget.dataChange.connect(self.build_tree_view)
        self.elevation_widget.show()
Beispiel #9
0
class QRAVEDockWidget(QDockWidget, Ui_QRAVEDockWidgetBase):

    closingPlugin = pyqtSignal()
    dataChange = pyqtSignal()
    showMeta = pyqtSignal()
    metaChange = pyqtSignal(str, str, dict, bool)

    def __init__(self, parent=None):
        """Constructor."""
        super(QRAVEDockWidget, self).__init__(parent)

        self.setupUi(self)
        self.menu = ContextMenu()
        self.qproject = QgsProject.instance()
        self.qproject.cleared.connect(self.close_all)

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(self.open_menu)
        self.treeView.doubleClicked.connect(self.default_tree_action)
        self.treeView.clicked.connect(self.item_change)

        self.treeView.expanded.connect(self.expand_tree_item)

        self.settings = Settings()

        self.model = QStandardItemModel()

        self.loaded_projects: List(Project) = []

        # Initialize our classes
        self.basemaps = BaseMaps()
        self.treeView.setModel(self.model)

        self.dataChange.connect(self.reload_tree)
        self.reload_tree()

    def expand_tree_item(self, idx: QModelIndex):
        item = self.model.itemFromIndex(idx)
        item_data = item.data(Qt.UserRole)
        if item_data and item_data.data and isinstance(item_data.data, QRaveBaseMap):
            item_data.data.load_layers()

    def _get_project(self, xml_path):
        try:
            return next(iter(self.loaded_projects))
        except Exception:
            return None

    @pyqtSlot()
    def reload_tree(self):
        # re-initialize our model and reload the projects from file
        # Try not to do this too often if you can
        self.model.clear()
        self.loaded_projects = []
        qrave_projects = self.get_project_settings()

        for project_path in qrave_projects:
            project = Project(project_path)
            project.load()

            if project is not None and project.exists is True and project.qproject is not None:
                self.model.appendRow(project.qproject)
                self.expand_children_recursive(self.model.indexFromItem(project.qproject))
                self.loaded_projects.append(project)

        # Load the tree objects
        self.basemaps.load()

        # Now load the basemaps
        region = self.settings.getValue('basemapRegion')
        if self.settings.getValue('basemapsInclude') is True \
                and region is not None and len(region) > 0 \
                and region in self.basemaps.regions.keys():
            self.model.appendRow(self.basemaps.regions[region])
            self.expand_children_recursive(self.model.indexFromItem(self.basemaps.regions[region]))

    def get_project_settings(self):
        try:
            qrave_projects_raw, type_conversion_ok = self.qproject.readEntry(
                CONSTANTS['settingsCategory'],
                'qrave_projects'
            )

            if type_conversion_ok is False:
                qrave_projects = []
            else:
                qrave_projects = json.loads(qrave_projects_raw)

                if qrave_projects is None or not isinstance(qrave_projects, list):
                    qrave_projects = []

        except Exception as e:
            self.settings.log('Error loading project settings: {}'.format(e), Qgis.Warning)
            qrave_projects = []

        filtered = [pf for pf in qrave_projects if os.path.isfile(pf)]
        filtered.reverse()
        # We Treat this like a stack where the last project in goes on the top.
        # Element 0 should be the top item
        return filtered

    def set_project_settings(self, projects: List[str]):
        self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'qrave_projects', json.dumps(projects))

    @pyqtSlot()
    def add_project(self, xml_path: str):
        qrave_projects = self.get_project_settings()

        # If this project is not already in
        if xml_path not in qrave_projects:
            qrave_projects.append(xml_path)
            self.set_project_settings(qrave_projects)
            self.reload_tree()

            new_project = self._get_project(xml_path)

            # If this is a fresh load and the setting is set we load the default view
            load_default_setting = self.settings.getValue('loadDefaultView')

            if load_default_setting is True \
                    and new_project.default_view is not None \
                    and new_project.default_view in new_project.views:
                self.add_children_to_map(new_project.qproject, new_project.views[new_project.default_view])

    def closeEvent(self, event):
        """ When the user clicks the "X" in the dockwidget titlebar
        """
        self.hide()
        self.qproject.removeEntry(CONSTANTS['settingsCategory'], 'enabled')
        self.closingPlugin.emit()
        event.accept()

    def expand_children_recursive(self, idx: QModelIndex = None, force=False):
        """Expand all the children of a QTreeView node. Do it recursively
        TODO: Recursion might not be the best for large trees here.

        Args:
            idx (QModelIndex, optional): [description]. Defaults to None.
            force: ignore the "collapsed" business logic attribute
        """
        if idx is None:
            idx = self.treeView.rootIndex()

        for idy in range(self.model.rowCount(idx)):
            child = self.model.index(idy, 0, idx)
            self.expand_children_recursive(child, force)

        item = self.model.itemFromIndex(idx)
        item_data = item.data(Qt.UserRole) if item is not None else None

        # NOTE: This is pretty verbose on purpose

        # This thing needs to have data or it defaults to being expanded
        if item_data is None or item_data.data is None:
            collapsed = False

        # Collapsed is an attribute set in the business logic
        # Never expand the QRaveBaseMap object becsause there's a network call involved
        elif isinstance(item_data.data, QRaveBaseMap) \
                or (isinstance(item_data.data, dict) and 'collapsed' in item_data.data and item_data.data['collapsed'] == 'true'):
            collapsed = True

        else:
            collapsed = False

        if not self.treeView.isExpanded(idx) and not collapsed:
            self.treeView.setExpanded(idx, True)

    def default_tree_action(self, idx: QModelIndex):
        if not idx.isValid():
            return

        item = self.model.itemFromIndex(idx)
        item_data: ProjectTreeData = item.data(Qt.UserRole)

        # This is the default action for all add-able layers including basemaps
        if isinstance(item_data.data, QRaveMapLayer):
            if item_data.data.layer_type in [QRaveMapLayer.LayerTypes.FILE, QRaveMapLayer.LayerTypes.REPORT]:
                self.file_system_open(item_data.data.layer_uri)
            else:
                QRaveMapLayer.add_layer_to_map(item)

        # Expand is the default option for wms because we might need to load the layers
        elif isinstance(item_data.data, QRaveBaseMap):
            if item_data.data.tile_type == 'wms':
                pass
            # All the XYZ layers can be added normally.
            else:
                QRaveMapLayer.add_layer_to_map(item)

        elif item_data.type in [QRaveTreeTypes.PROJECT_ROOT]:
            self.change_meta(item, item_data, True)

        # For folder-y types we want Expand and contract is already implemented as a default
        elif item_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            # print("Default Folder Action")
            pass

        elif item_data.type == QRaveTreeTypes.PROJECT_VIEW:
            print("Default View Action")
            self.add_view_to_map(item_data)

    def item_change(self, pos):
        """Triggered when the user selects a new item in the tree

        Args:pos
            pos ([type]): [description]
        """
        indexes = self.treeView.selectedIndexes()

        # No multiselect so there is only ever one item
        item = self.model.itemFromIndex(indexes[0])
        data_item: ProjectTreeData = item.data(Qt.UserRole)

        if len(indexes) < 1 or data_item.project is None or not data_item.project.exists:
            return

        # Update the metadata if we need to
        self.change_meta(item, data_item)

    def change_meta(self, item: QStandardItem, item_data: ProjectTreeData, show=False):
        """Update the MetaData dock widget with new information

        Args:
            item (QStandardItem): [description]
            data ([type]): [description]
            show (bool, optional): [description]. Defaults to False.
        """
        data = item_data.data
        if isinstance(data, QRaveMapLayer):
            meta = data.meta if data.meta is not None else {}
            self.metaChange.emit(item.text(), MetaType.LAYER, meta, show)

        elif isinstance(data, QRaveBaseMap):
            self.metaChange.emit(item.text(), MetaType.NONE, {}, show)

        elif item_data.type == QRaveTreeTypes.PROJECT_ROOT:
            self.metaChange.emit(item.text(), MetaType.PROJECT, {
                'project': item_data.project.meta,
                'warehouse': item_data.project.warehouse_meta
            }, show)
        elif item_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            self.metaChange.emit(item.text(), MetaType.FOLDER, data or {}, show)
        elif isinstance(data, dict):
            # this is just the generic case for any kind of metadata
            self.metaChange.emit(item.text(), MetaType.NONE, data or {}, show)
        else:
            # Do not  update the metadata if we have nothing to show
            self.metaChange.emit(item.text(), MetaType.NONE, {}, show)

    def get_warehouse_url(self, wh_meta: Dict[str, str]):

        if wh_meta is not None:

            if 'program' in wh_meta and 'id' in wh_meta:
                return '/'.join([CONSTANTS['warehouseUrl'], wh_meta['program'], wh_meta['id']])

            elif '_rs_wh_id' in wh_meta and '_rs_wh_program' in wh_meta:
                return '/'.join([CONSTANTS['warehouseUrl'], wh_meta['_rs_wh_program'], wh_meta['_rs_wh_id']])

        return None

    def project_warehouse_view(self, project: Project):
        """Open this project in the warehouse if the warehouse meta entries exist
        """
        url = self.get_warehouse_url(project.warehouse_meta)
        if url is not None:
            QDesktopServices.openUrl(QUrl(url))

    def layer_warehouse_view(self, data: QRaveMapLayer):
        """Open this project in the warehouse if the warehouse meta entries exist
        """
        url = self.get_warehouse_url(data.meta)
        if url is not None:
            QDesktopServices.openUrl(QUrl(url))

    def close_all(self):
        for p in range(len(self.loaded_projects)):
            self.close_project(self.loaded_projects[0])

    def close_project(self, project: Project):
        """ Close the project
        """
        try:
            qrave_projects_raw, type_conversion_ok = self.qproject.readEntry(
                CONSTANTS['settingsCategory'],
                'qrave_projects'
            )
            qrave_projects = json.loads(qrave_projects_raw)
            if not type_conversion_ok or qrave_projects is None:
                qrave_projects = []
        except Exception as e:
            self.settings.log('Error closing project: {}'.format(e), Qgis.Warning)
            qrave_projects = []

        # Filter out the project we want to close and reload the tree
        qrave_projects = [x.project_xml_path for x in self.loaded_projects if x != project]

        # Write the settings back to the project
        self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'qrave_projects', json.dumps(qrave_projects))
        self.loaded_projects = qrave_projects
        self.reload_tree()

    def file_system_open(self, fpath: str):
        """Open a file on the operating system using the default action

        Args:
            fpath (str): [description]
        """
        qurl = QUrl.fromLocalFile(fpath)
        QDesktopServices.openUrl(QUrl(qurl))

    def file_system_locate(self, fpath: str):
        """This the OS-agnostic "show in Finder" or "show in explorer" equivalent
        It should open the folder of the item in question

        Args:
            fpath (str): [description]
        """
        final_path = os.path.dirname(fpath)
        while not os.path.isdir(final_path):
            final_path = os.path.dirname(final_path)

        qurl = QUrl.fromLocalFile(final_path)
        QDesktopServices.openUrl(qurl)

    def toggleSubtree(self, item: QStandardItem = None, expand=True):

        def _recurse(curritem):
            idx = self.model.indexFromItem(item)
            if expand is not self.treeView.isExpanded(idx):
                self.treeView.setExpanded(idx, expand)

            for row in range(curritem.rowCount()):
                _recurse(curritem.child(row))

        if item is None:
            if expand is True:
                self.treeView.expandAll()
            else:
                self.treeView.collapseAll()
        else:
            _recurse(item)

    def add_view_to_map(self, item_data: ProjectTreeData):
        """Add a view and all its layers to the map

        Args:
            item (QStandardItem): [description]
        """
        self.add_children_to_map(item_data.project.qproject, item_data.data)

    def add_children_to_map(self, item: QStandardItem, bl_ids: List[str] = None):
        """Iteratively add all children to the map

        Args:
            item (QStandardItem): [description]
            bl_ids (List[str], optional): List of ids to filter by so we don't load everything. this is used for loading views
        """

        for child in self._get_children(item):
            # Is this something we can add to the map?
            project_tree_data = child.data(Qt.UserRole)
            if project_tree_data is not None and isinstance(project_tree_data.data, QRaveMapLayer):
                data = project_tree_data.data
                loadme = False
                # If this layer matches the businesslogic id filter
                if bl_ids is not None and len(bl_ids) > 0:
                    if 'id' in data.bl_attr and data.bl_attr['id'] in bl_ids:
                        loadme = True
                else:
                    loadme = True

                if loadme is True:
                    data.add_layer_to_map(child)

    def _get_children(self, root_item: QStandardItem):
        """Recursion is going to kill us here so do an iterative solution instead
           https://stackoverflow.com/questions/41949370/collect-all-items-in-qtreeview-recursively

        Yields:
            [type]: [description]
        """
        stack = [root_item]
        while stack:
            parent = stack.pop(0)
            for row in range(parent.rowCount()):
                for column in range(parent.columnCount()):
                    child = parent.child(row, column)
                    yield child
                    if child.hasChildren():
                        stack.append(child)

    def _get_parents(self, start_item: QStandardItem):
        stack = []
        placeholder = start_item.parent()
        while placeholder is not None and placeholder != self.model.invisibleRootItem():
            stack.append(placeholder)
            placeholder = start_item.parent()

        return stack.reverse()

    def open_menu(self, position):

        indexes = self.treeView.selectedIndexes()
        if len(indexes) < 1:
            return

        # No multiselect so there is only ever one item
        idx = indexes[0]

        if not idx.isValid():
            return

        item = self.model.itemFromIndex(indexes[0])
        project_tree_data = item.data(Qt.UserRole)  # ProjectTreeData object
        data = project_tree_data.data  # Could be a QRaveBaseMap, a QRaveMapLayer or just some random data

        # This is the layer context menu
        if isinstance(data, QRaveMapLayer):
            if data.layer_type == QRaveMapLayer.LayerTypes.WEBTILE:
                self.basemap_context_menu(idx, item, project_tree_data)
            elif data.layer_type in [QRaveMapLayer.LayerTypes.FILE, QRaveMapLayer.LayerTypes.REPORT]:
                self.file_layer_context_menu(idx, item, project_tree_data)
            else:
                self.map_layer_context_menu(idx, item, project_tree_data)

        elif isinstance(data, QRaveBaseMap):
            # A WMS QARaveBaseMap is just a container for layers
            if data.tile_type == 'wms':
                self.folder_dumb_context_menu(idx, item, project_tree_data)
            # Every other kind of basemap is an add-able layer
            else:
                self.basemap_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type == QRaveTreeTypes.PROJECT_ROOT:
            self.project_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type in [
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER
        ]:
            self.folder_dumb_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            self.folder_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type == QRaveTreeTypes.PROJECT_VIEW:
            self.view_context_menu(idx, item, project_tree_data)

        self.menu.exec_(self.treeView.viewport().mapToGlobal(position))

    def map_layer_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_TO_MAP', lambda: QRaveMapLayer.add_layer_to_map(item), enabled=item_data.data.exists)
        self.menu.addAction('VIEW_LAYER_META', lambda: self.change_meta(item, item_data, True))

        if bool(self.get_warehouse_url(item_data.data.meta)):
            self.menu.addAction('VIEW_WEB_SOURCE', lambda: self.layer_warehouse_view(item_data))

        self.menu.addAction('BROWSE_FOLDER', lambda: self.file_system_locate(item_data.data.layer_uri))

    def file_layer_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('OPEN_FILE', lambda: self.file_system_open(item_data.data.layer_uri))
        self.menu.addAction('BROWSE_FOLDER', lambda: self.file_system_locate(item_data.data.layer_uri))

    # Basemap context items
    def basemap_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_TO_MAP', lambda: QRaveMapLayer.add_layer_to_map(item))

    # Folder-level context menu
    def folder_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_children_to_map(item))
        self.menu.addSeparator()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(item, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(item, True))

    # Some folders don't have the 'ADD_ALL_TO_MAP' functionality enabled
    def folder_dumb_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(item, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(item, True))

    # View context items
    def view_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_view_to_map(item_data))

    # Project-level context menu
    def project_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(None, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(None, True))

        self.menu.addSeparator()
        self.menu.addAction('BROWSE_PROJECT_FOLDER', lambda: self.file_system_locate(data.project.project_xml_path))
        self.menu.addAction('VIEW_PROJECT_META', lambda: self.change_meta(item, data, True))
        self.menu.addAction('WAREHOUSE_VIEW', lambda: self.project_warehouse_view(data.project), enabled=bool(self.get_warehouse_url(data.project.warehouse_meta)))
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_children_to_map(item))
        self.menu.addSeparator()
        self.menu.addAction('REFRESH_PROJECT_HIERARCHY', self.reload_tree)
        self.menu.addAction('CUSTOMIZE_PROJECT_HIERARCHY', enabled=False)
        self.menu.addSeparator()
        self.menu.addAction('CLOSE_PROJECT', lambda: self.close_project(data.project), enabled=bool(data.project))