Compare commits

..

No commits in common. "main" and "1.34" have entirely different histories.
main ... 1.34

4 changed files with 168 additions and 304 deletions

View File

@ -1,31 +1,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
Module Flux Editor pour le plugin CenRA_FLUX
Permet de gérer et charger des couches depuis les bases de données PostGIS (SIG et REF)
"""
from __future__ import absolute_import from __future__ import absolute_import
# Import des bibliothèques PyQt et QGIS # Import the PyQt and QGIS libraries
# from builtins import next
from builtins import str from builtins import str
# from builtins import object
from qgis.PyQt import QtCore, QtGui from qgis.PyQt import QtCore, QtGui
from qgis.PyQt.QtCore import QSettings from qgis.PyQt.QtCore import QSettings
from qgis.PyQt import QtWidgets from qgis.PyQt import QtWidgets
# from qgis.PyQt.QtWidgets import QDialog
from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtGui import QIcon
from qgis.core import ( # from qgis.PyQt.QtCore import *
QgsDataSourceUri,
QgsCoordinateReferenceSystem, # from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsSettings
QgsCoordinateTransform, from qgis.core import QgsDataSourceUri, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsSettings, QgsApplication, QgsVectorLayer, QgsRasterLayer, QgsWkbTypes
QgsProject,
QgsSettings,
QgsApplication,
QgsVectorLayer,
QgsRasterLayer,
QgsWkbTypes
)
from qgis.PyQt.QtWidgets import ( from qgis.PyQt.QtWidgets import (
QDialog, QDialog,
# QAction,
# QDockWidget,
# QFileDialog,
# QInputDialog,
# QMenu,
# QToolButton,
# QTableWidget,
QPushButton, QPushButton,
QTableWidgetItem, QTableWidgetItem,
QMessageBox, QMessageBox,
@ -35,21 +34,28 @@ from .tools.SQLRequet import schemaname_list, schemaname_list_ref, schemaname_di
from .tools.resources import ( from .tools.resources import (
load_ui, load_ui,
resources_path, resources_path,
# send_issues,
) )
try: try:
from .tools.PythonSQL import login_base from .tools.PythonSQL import login_base
except NameError: except NameError:
print('Pas de fichier PythonSQL') print('Pas de fichier PythonSQL')
# from .issues import CenRa_Issues
# from ast import literal_eval
from qgis.utils import iface from qgis.utils import iface
# import os.path
# import os
# import webbrowser
import psycopg2 import psycopg2
import psycopg2.extras import psycopg2.extras
# import base64
# Variable globale pour le mode debug
global DeBUG global DeBUG
DeBUG = 0 DeBUG = 0
# Création des icônes pour les couches raster et vecteur
itemIconRaster = QTableWidgetItem() itemIconRaster = QTableWidgetItem()
icon = QIcon() icon = QIcon()
icon.addPixmap(QtGui.QPixmap(resources_path('icons', 'mIconRaster.svg')), QIcon.Mode(0), QIcon.State(1)) icon.addPixmap(QtGui.QPixmap(resources_path('icons', 'mIconRaster.svg')), QIcon.Mode(0), QIcon.State(1))
@ -60,36 +66,28 @@ icon = QIcon()
icon.addPixmap(QtGui.QPixmap(resources_path('icons', 'mIconVecteur.svg')), QIcon.Mode(0), QIcon.State(1)) icon.addPixmap(QtGui.QPixmap(resources_path('icons', 'mIconVecteur.svg')), QIcon.Mode(0), QIcon.State(1))
itemIconVecteur.setIcon(icon) itemIconVecteur.setIcon(icon)
# Récupération des informations de connexion à la base de données
try: try:
account = login_base('account') account = login_base('account')
user = account[0] # Nom d'utilisateur user = account[0]
mdp = account[1] # Mot de passe mdp = account[1]
host = account[2] # Hôte de la base de données host = account[2]
port = account[3] # Port de connexion port = account[3]
dbname = account[4] # Nom de la base de données dbname = account[4]
sigdb = account[5] # Base de données SIG sigdb = account[5]
refdb = account[6] # Base de données REF refdb = account[6]
except NameError: except NameError:
print('Fails to login DB for account') print('Fails to login DB for account')
# Chargement de l'interface utilisateur depuis le fichier .ui
EDITOR_CLASS = load_ui('CenRa_Flux_base.ui') EDITOR_CLASS = load_ui('CenRa_Flux_base.ui')
# Configuration des systèmes de coordonnées de référence (CRS) targetCrs = QgsCoordinateReferenceSystem('EPSG:4326')
targetCrs = QgsCoordinateReferenceSystem('EPSG:4326') # WGS84 (coordonnées géographiques) layerCrs = QgsCoordinateReferenceSystem('EPSG:2154')
layerCrs = QgsCoordinateReferenceSystem('EPSG:2154') # Lambert 93 (projection française)
TranformCRS = QgsCoordinateTransform(layerCrs, targetCrs, QgsProject.instance()) TranformCRS = QgsCoordinateTransform(layerCrs, targetCrs, QgsProject.instance())
class Flux_Editor(QDialog, EDITOR_CLASS): class Flux_Editor(QDialog, EDITOR_CLASS):
"""
Classe principale de l'éditeur de flux
Gère l'interface de sélection et de chargement des couches depuis les bases de données
"""
def __init__(self, parent=None): def __init__(self, parent=None):
"""Initialisation de l'interface et des composants"""
_ = parent _ = parent
super().__init__() super().__init__()
self.setupUi(self) self.setupUi(self)
@ -98,36 +96,31 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.setWindowIcon(QtGui.QIcon(resources_path('icons', 'icon.png'))) self.setWindowIcon(QtGui.QIcon(resources_path('icons', 'icon.png')))
self.first_start = None self.first_start = None
self.iface = iface self.iface = iface
# Configuration des éléments visuels de l'interface
self.label_3.setPixmap(QtGui.QPixmap(resources_path('ui', 'logo.png'))) self.label_3.setPixmap(QtGui.QPixmap(resources_path('ui', 'logo.png')))
self.commandLinkButton.setIcon(QtGui.QIcon(resources_path('ui', 'arrow-bottom.png'))) self.commandLinkButton.setIcon(QtGui.QIcon(resources_path('ui', 'arrow-bottom.png')))
self.commandLinkButton_2.setIcon(QtGui.QIcon(resources_path('ui', 'arrow-up.png'))) self.commandLinkButton_2.setIcon(QtGui.QIcon(resources_path('ui', 'arrow-up.png')))
# Configuration du tableau des couches disponibles
self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger(0)) self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger(0))
self.toolButton.setIcon(QtGui.QIcon(resources_path('ui', 'find.png'))) self.toolButton.setIcon(QtGui.QIcon(resources_path('ui', 'find.png')))
self.toolButton_2.setIcon(QtGui.QIcon(resources_path('ui', 'star.png'))) self.toolButton_2.setIcon(QtGui.QIcon(resources_path('ui', 'star.png')))
# Ajout des options de base de données (SIG ou REF)
self.comboBox_2.addItem("SIG") self.comboBox_2.addItem("SIG")
self.comboBox_2.addItem('REF') self.comboBox_2.addItem('REF')
# Connexion des signaux aux slots (événements de l'interface) self.commandLinkButton.clicked.connect(self.selection_flux)
self.commandLinkButton.clicked.connect(self.selection_flux) # Bouton de sélection de flux self.tableWidget.itemDoubleClicked.connect(self.selection_flux)
self.tableWidget.itemDoubleClicked.connect(self.selection_flux) # Double-clic sur une couche self.pushButton_2.clicked.connect(self.limite_flux)
self.pushButton_2.clicked.connect(self.limite_flux) # Bouton de chargement self.commandLinkButton_2.clicked.connect(self.suppression_flux)
self.commandLinkButton_2.clicked.connect(self.suppression_flux) # Bouton de suppression self.tableWidget_2.itemDoubleClicked.connect(self.suppression_flux)
self.tableWidget_2.itemDoubleClicked.connect(self.suppression_flux) # Double-clic pour supprimer self.comboBox_2.currentIndexChanged.connect(self.bd_source)
self.comboBox_2.currentIndexChanged.connect(self.bd_source) # Changement de base de données
self.checkBox.hide() self.checkBox.hide()
self.toolButton.clicked.connect(self.getCanevas) # Filtrer par emprise du canevas # self.checkBox.stateChanged.connect(self.modeCarte)
self.toolButton.setToolTip('Filtrer uniquement les couche Contour/Habitat/Travaux \nqui ce trouve dans le canvas de la carte.') self.toolButton.clicked.connect(self.getCanevas)
self.toolButton_2.clicked.connect(self.filtre_favorit) # Filtrer par favoris self.toolButton_2.clicked.connect(self.filtre_favorit)
self.toolButton_2.setToolTip('Afficher uniquement les favoris.')
layout = QVBoxLayout() layout = QVBoxLayout()
self.lineEdit.textChanged.connect(self.filtre_dynamique) # Recherche dynamique self.lineEdit.textChanged.connect(self.filtre_dynamique)
# self.viewer.textChanged.connect(self.NewTitle)
layout.addWidget(self.lineEdit) layout.addWidget(self.lineEdit)
self.viewer.hide() self.viewer.hide()
# Configuration du menu de debug (caché par défaut)
self.DeBUG.addItem('') self.DeBUG.addItem('')
self.DeBUG.addItem('Dev') self.DeBUG.addItem('Dev')
self.DeBUG.addItem('01') self.DeBUG.addItem('01')
@ -136,14 +129,13 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.comboBox.currentIndexChanged.connect(self.initialisation_flux) self.comboBox.currentIndexChanged.connect(self.initialisation_flux)
self.DeBUG.currentIndexChanged.connect(self.SwitchDEBUG) self.DeBUG.currentIndexChanged.connect(self.SwitchDEBUG)
self.DeBUG.hide() # Menu debug caché par défaut self.DeBUG.hide()
def raise_(self): def raise_(self):
"""Run method that performs all the real work""" """Run method that performs all the real work"""
self.bd_source() self.bd_source()
def SwitchDEBUG(self): def SwitchDEBUG(self):
"""Change les paramètres de connexion selon le mode debug sélectionné"""
try: try:
global user, mdp, host, port, dbname, sigdb, refdb global user, mdp, host, port, dbname, sigdb, refdb
account = login_base(self.DeBUG.currentText()) account = login_base(self.DeBUG.currentText())
@ -163,36 +155,39 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.DeBUG.show() self.DeBUG.show()
def mousePressEvent(self, event): def mousePressEvent(self, event):
"""Détecte les clics sur le logo pour activer le mode debug (3 clics consécutifs)"""
global DeBUG global DeBUG
# Zone du logo (coordonnées x: 330-560, y: 5-75)
if 330 <= event.pos().x() <= 560 and 5 <= event.pos().y() <= 75: if 330 <= event.pos().x() <= 560 and 5 <= event.pos().y() <= 75:
DeBUG = DeBUG + 1 DeBUG = DeBUG + 1
if DeBUG == 3: # Activation après 3 clics if DeBUG == 3:
DeBUG = 0 DeBUG = 0
self.ModeDeBUG() self.ModeDeBUG()
else: else:
DeBUG = 0 DeBUG = 0
self.DeBUG.hide() self.DeBUG.hide()
"""
def NewTitle(self):
if self.viewer.title() != '':
Tsplit = ((self.viewer.title()).split('.'))
print(Tsplit[0], Tsplit[1])
self.openPostGIS(Tsplit[0], Tsplit[1])
"""
def bd_source(self): def bd_source(self):
"""Sélectionne la base de données source (SIG ou REF)"""
self.activateWindow() self.activateWindow()
bd_origine = self.comboBox_2.currentText() bd_origine = self.comboBox_2.currentText()
if bd_origine == 'REF': if bd_origine == 'REF':
self.run_ref() # Connexion à la base REF self.run_ref()
if bd_origine == 'SIG': if bd_origine == 'SIG':
self.run_sig() # Connexion à la base SIG self.run_sig()
def run_ref(self): def run_ref(self):
"""Initialise la connexion à la base de données REF""" """Run method that performs all the real work"""
# Vider le tableau des flux sélectionnés
while self.tableWidget_2.rowCount() > 0: while self.tableWidget_2.rowCount() > 0:
self.tableWidget_2.removeRow(self.tableWidget_2.rowCount() - 1) self.tableWidget_2.removeRow(self.tableWidget_2.rowCount() - 1)
# print(self.tableWidget_2.rowCount())
global cur, con, dbtype global cur, con, dbtype
dbtype = refdb dbtype = refdb
# Connexion à la base de données REF
con = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + dbtype + " user=" + user + " password=" + mdp) con = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + dbtype + " user=" + user + " password=" + mdp)
cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor) cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor)
self.combobox_custom() self.combobox_custom()
@ -205,69 +200,63 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.show() self.show()
# Run the dialog event loop # Run the dialog event loop
result = self.exec() result = self.exec()
# See if OK was pressed
if result: if result:
# Do something useful here - delete the line containing pass and
# substitute with your code.
pass pass
def run_sig(self): def run_sig(self):
"""Initialise la connexion à la base de données SIG""" """Run method that performs all the real work"""
# Vider le tableau des flux sélectionnés
while self.tableWidget_2.rowCount() > 0: while self.tableWidget_2.rowCount() > 0:
self.tableWidget_2.removeRow(self.tableWidget_2.rowCount() - 1) self.tableWidget_2.removeRow(self.tableWidget_2.rowCount() - 1)
global cur, con, dbtype global cur, con, dbtype
dbtype = sigdb dbtype = sigdb
# Connexion à la base de données SIG
con = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + dbtype + " user=" + user + " password=" + mdp) con = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + dbtype + " user=" + user + " password=" + mdp)
cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor) cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor)
self.combobox_custom() self.combobox_custom()
self.initialisation_flux() self.initialisation_flux()
# Create the dialog with elements (after translation) and keep reference
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
if self.first_start is True: if self.first_start is True:
self.first_start = False self.first_start = False
# show the dialog
# self.show()
# Run the dialog event loop
# result = self.exec()
# See if OK was pressed
# if result:
# Do something useful here - delete the line containing pass and
# substitute with your code.
# pass
def suppression_flux(self): def suppression_flux(self):
self.tableWidget_2.removeRow(self.tableWidget_2.currentRow()) self.tableWidget_2.removeRow(self.tableWidget_2.currentRow())
def AddOrDelToUserFav(self): def AddOrDelToUserFav(self):
"""Ajoute ou supprime une couche des favoris de l'utilisateur"""
selected_items = self.tableWidget.selectedItems() selected_items = self.tableWidget.selectedItems()
favorit_statut = selected_items[4].text() # 0 = non favori, 1 = favori favorit_statut = selected_items[4].text()
selected_items[4].tableWidget().removeCellWidget(selected_items[4].row(), 4) selected_items[4].tableWidget().removeCellWidget(selected_items[4].row(), 4)
schema_name = selected_items[2].text() schema_name = selected_items[2].text()
table_name = selected_items[3].text() table_name = selected_items[3].text()
# Ajout aux favoris
if favorit_statut == "0": if favorit_statut == "0":
FAV = "mStarIconDel.png" # Icône étoile pleine FAV = "mStarIconDel.png"
selected_items[4].setText("1") selected_items[4].setText("1")
# Requête SQL pour ajouter aux favoris
SQLAddFav = """INSERT INTO admin_sig.favtable (utilisateur, schema_name, table_name) VALUES ('""" + user + "','" + schema_name + "','" + table_name + """');""" SQLAddFav = """INSERT INTO admin_sig.favtable (utilisateur, schema_name, table_name) VALUES ('""" + user + "','" + schema_name + "','" + table_name + """');"""
if dbtype == sigdb: if dbtype == sigdb:
cur.execute(SQLAddFav) cur.execute(SQLAddFav)
con.commit() con.commit()
else:
conSIG = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + sigdb + " user=" + user + " password=" + mdp)
curSIG = conSIG.cursor(cursor_factory=psycopg2.extras.DictCursor)
curSIG.execute(SQLAddFav)
conSIG.commit()
conSIG.close()
# Suppression des favoris
else: else:
FAV = "mStarIconAdd.png" # Icône étoile vide FAV = "mStarIconAdd.png"
selected_items[4].setText("0") selected_items[4].setText("0")
# Requête SQL pour supprimer des favoris
SQLDelFav = """DELETE FROM admin_sig.favtable WHERE utilisateur LIKE '""" + user + """' AND schema_name LIKE '""" + schema_name + """' AND table_name LIKE '""" + table_name + """';""" SQLDelFav = """DELETE FROM admin_sig.favtable WHERE utilisateur LIKE '""" + user + """' AND schema_name LIKE '""" + schema_name + """' AND table_name LIKE '""" + table_name + """';"""
if dbtype == sigdb: if dbtype == sigdb:
cur.execute(SQLDelFav) cur.execute(SQLDelFav)
con.commit() con.commit()
else:
conSIG = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + sigdb + " user=" + user + " password=" + mdp)
curSIG = conSIG.cursor(cursor_factory=psycopg2.extras.DictCursor)
curSIG.execute(SQLDelFav)
conSIG.commit()
conSIG.close()
iconFav = QIcon() iconFav = QIcon()
iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1)) iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1))
@ -276,39 +265,33 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.FavButton.clicked.connect(self.AddOrDelToUserFav) self.FavButton.clicked.connect(self.AddOrDelToUserFav)
def initialisation_flux(self): def initialisation_flux(self):
"""Initialise et remplit le tableau des couches disponibles"""
if self.toolButton_2.text() == "1":
self.filtre_favorit(None)
self.tableWidget.clear() self.tableWidget.clear()
if NoSignals == 0: # Éviter les boucles infinies lors des mises à jour if NoSignals == 0:
# self.tableWidget.clear()
# print('Initiaisation de la connection a la DB: ' + dbtype)
if dbtype == sigdb: if dbtype == sigdb:
# self.toolButton.setEnabled(1)
if self.comboBox.currentText() == 'toutes les catégories': if self.comboBox.currentText() == 'toutes les catégories':
custom_list = schemaname_list custom_list = schemaname_list
elif self.comboBox.currentText() == 'travaux': elif self.comboBox.currentText() == 'travaux':
custom_list = schemaname_list[:-2] + """ custom_list = """(SELECT schemaname,tablename from pg_catalog.pg_tables
WHERE schemaname LIKE '""" + str(self.comboBox.currentText()) + """%'; where schemaname like '""" + str(self.comboBox.currentText()) + """%' order by schemaname,tablename) UNION (SELECT schemaname,matviewname AS tablename FROM pg_catalog.pg_matviews where schemaname like '""" + str(self.comboBox.currentText()) + """%' order by schemaname,tablename) order by schemaname,tablename;"""
"""
else: else:
custom_list = schemaname_list[:-2] + """ custom_list = """(SELECT schemaname,tablename from pg_catalog.pg_tables
WHERE schemaname LIKE '_""" + str(self.comboBox.currentText()) + """%'; where schemaname like '\\_""" + str(self.comboBox.currentText()) + """%' order by schemaname,tablename) UNION (SELECT schemaname,matviewname AS tablename FROM pg_catalog.pg_matviews where schemaname like '\\_""" + str(self.comboBox.currentText()) + """%' order by schemaname,tablename) order by schemaname,tablename;"""
"""
else: else:
if self.comboBox.currentText() == 'toutes les catégories': if self.comboBox.currentText() == 'toutes les catégories':
custom_list = schemaname_list_ref custom_list = schemaname_list_ref
else: else:
custom_list = schemaname_list_ref[:-2] + """ custom_list = """SELECT schemaname, tablename from pg_catalog.pg_tables WHERE schemaname LIKE '""" + str(self.comboBox.currentText()) + """' AND tablename NOT LIKE 'qgis_projects' order by schemaname, tablename;"""
WHERE schemaname LIKE '""" + str(self.comboBox.currentText()) + """%' AND tablename NOT LIKE 'qgis_projects';
"""
cur.execute(custom_list) cur.execute(custom_list)
list_schema = cur.fetchall() list_schema = cur.fetchall()
# Vérification de la présence de couches raster dans la base
SQLcountRaster = """SELECT schemaname,viewname FROM pg_catalog.pg_views SQLcountRaster = """SELECT schemaname,viewname FROM pg_catalog.pg_views
WHERE schemaname LIKE 'public' AND viewname LIKE 'raster_columns';""" WHERE schemaname LIKE 'public' AND viewname LIKE 'raster_columns';"""
cur.execute(SQLcountRaster) cur.execute(SQLcountRaster)
RasterIF = len(cur.fetchall()) RasterIF = len(cur.fetchall())
# Récupération de la liste des couches raster si disponibles
if RasterIF == 1: if RasterIF == 1:
SQLloadRaster = """SELECT concat(r_table_schema,'.',r_table_name) from public.raster_columns; """ SQLloadRaster = """SELECT concat(r_table_schema,'.',r_table_name) from public.raster_columns; """
cur.execute(SQLloadRaster) cur.execute(SQLloadRaster)
@ -319,7 +302,6 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
else: else:
RasterList = [] RasterList = []
# Récupération des projets QGIS stockés dans la base de données
SQLprojects = """SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE tablename LIKE 'qgis_projects'""" SQLprojects = """SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE tablename LIKE 'qgis_projects'"""
cur.execute(SQLprojects) cur.execute(SQLprojects)
list_projects = cur.fetchall() list_projects = cur.fetchall()
@ -330,65 +312,33 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
cur.execute(SQLProjectsQgis) cur.execute(SQLProjectsQgis)
list_projects_qgis.append(cur.fetchall()) list_projects_qgis.append(cur.fetchall())
# Récupération des droits d'accès de l'utilisateur sur les tables
SQLGrands = """
WITH t as (SELECT
n.nspname AS table_schema,
c.relname AS table_name,
r.rolname AS grantee,
CASE
WHEN acl_text LIKE '%r%' THEN 'SELECT'
WHEN acl_text LIKE '%w%' THEN 'UPDATE'
WHEN acl_text LIKE '%a%' THEN 'INSERT'
WHEN acl_text LIKE '%d%' THEN 'DELETE'
WHEN acl_text LIKE '%x%' THEN 'REFERENCES'
ELSE 'OTHER'
END AS privilege_type
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN LATERAL unnest(c.relacl) AS acl_item(acl) ON TRUE
LEFT JOIN LATERAL (
SELECT acl::text AS acl_text,
split_part(acl::text, '=', 1) AS grantee_name
) priv ON TRUE
LEFT JOIN pg_roles r ON r.rolname = priv.grantee_name
WHERE c.relkind IN ('r', 'v','m'))
"""
if self.comboBox.currentText() == 'toutes les catégories': if self.comboBox.currentText() == 'toutes les catégories':
SQLGrands = SQLGrands + """SELECT concat(table_schema,'.',table_name) FROM t WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT';""" SQLGrands = """SELECT concat(table_schema,'.',table_name) FROM information_schema.role_table_grants WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT';"""
else: else:
if dbtype == sigdb: if dbtype == sigdb:
SQLGrands = SQLGrands + """SELECT concat(table_schema,'.',table_name) FROM t WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT' AND table_schema LIKE '_""" + str(self.comboBox.currentText()) + """_%';""" SQLGrands = """SELECT concat(table_schema,'.',table_name) FROM information_schema.role_table_grants WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT' AND table_schema LIKE '_""" + str(self.comboBox.currentText()) + """_%';"""
elif dbtype == refdb: elif dbtype == refdb:
SQLGrands = SQLGrands + """SELECT concat(table_schema,'.',table_name) FROM t WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT' AND table_schema LIKE '""" + str(self.comboBox.currentText()) + """%';""" SQLGrands = """SELECT concat(table_schema,'.',table_name) FROM information_schema.role_table_grants WHERE grantee in(SELECT rolname FROM pg_catalog.pg_roles WHERE oid in(SELECT roleid FROM pg_auth_members WHERE member = (SELECT usesysid FROM pg_catalog.pg_user WHERE usename = '""" + user + """'))) and privilege_type = 'SELECT' AND table_schema LIKE '""" + str(self.comboBox.currentText()) + """%';"""
cur.execute(SQLGrands) cur.execute(SQLGrands)
list_grands = cur.fetchall() list_grands = cur.fetchall()
GrandUser = [] GrandUser = []
for grandsFind in list_grands: for grandsFind in list_grands:
GrandUser.append(grandsFind[0]) GrandUser.append(grandsFind[0])
# Récupération de la liste des favoris de l'utilisateur if dbtype == sigdb:
SQLFavTable = "SELECT concat(schema_name, '.', table_name) FROM admin_sig.favtable WHERE utilisateur LIKE '" + user + "';" SQLFavTable = "SELECT concat(schema_name, '.', table_name) FROM admin_sig.favtable WHERE utilisateur LIKE '" + user + "';"
if dbtype == refdb:
conSIG = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + sigdb + " user=" + user + " password=" + mdp)
curSIG = conSIG.cursor(cursor_factory=psycopg2.extras.DictCursor)
curSIG.execute(SQLFavTable)
list_fav = curSIG.fetchall()
conSIG.close()
print('SIG')
else:
cur.execute(SQLFavTable) cur.execute(SQLFavTable)
list_fav = cur.fetchall() list_fav = cur.fetchall()
FavList = [] FavList = []
for favFind in list_fav: for favFind in list_fav:
FavList.append(favFind[0]) FavList.append(favFind[0])
else:
FavList = []
# Remplissage du tableau avec les couches disponibles
self.tableWidget.setRowCount(len(list_schema)) self.tableWidget.setRowCount(len(list_schema))
self.tableWidget.setColumnCount(5) self.tableWidget.setColumnCount(5)
i = 0 i = 0
for value in list_schema: for value in list_schema:
MetadataXML = value[2]
if dbtype == sigdb: if dbtype == sigdb:
type_val = str(value[0])[1:3] type_val = str(value[0])[1:3]
schema_name = str(value[0])[4:] schema_name = str(value[0])[4:]
@ -397,8 +347,6 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
table_name = str(value[1][len(value[0]) + 1:]) table_name = str(value[1][len(value[0]) + 1:])
else: else:
table_name = str(value[1]) table_name = str(value[1])
if type_val == "00":
table_name = value[1]
else: else:
type_val = '' type_val = ''
schema_name = str(value[0]) schema_name = str(value[0])
@ -430,6 +378,8 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1)) iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1))
self.FavButton = QPushButton(iconFav, "") self.FavButton = QPushButton(iconFav, "")
self.FavButton.clicked.connect(self.AddOrDelToUserFav) self.FavButton.clicked.connect(self.AddOrDelToUserFav)
if dbtype == refdb:
self.FavButton.setEnabled(False)
if (str(value[0]) + '.' + str(value[1])) in RasterList: if (str(value[0]) + '.' + str(value[1])) in RasterList:
SVG = 'mIconRaster.svg' SVG = 'mIconRaster.svg'
@ -452,12 +402,11 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.tableWidget.setItem(i, 4, item) self.tableWidget.setItem(i, 4, item)
self.tableWidget.setCellWidget(i, 4, self.FavButton) self.tableWidget.setCellWidget(i, 4, self.FavButton)
# Coloration des lignes selon les droits d'accès # if dbtype == sigdb:
if (str(value[0]) + '.' + str(value[1])) in GrandUser: # L'utilisateur a les droits if (str(value[0]) + '.' + str(value[1])) in GrandUser:
for j in range(self.tableWidget.columnCount()): pass
self.tableWidget.item(i, j).setToolTip(MetadataXML)
else: else:
# Coloration en violet si droits insuffisants # print(str(value[0]) + '.' + str(value[1]), 'bad')
for j in range(self.tableWidget.columnCount()): for j in range(self.tableWidget.columnCount()):
self.tableWidget.item(i, j).setBackground(QtGui.QColor(187, 134, 192, 50)) self.tableWidget.item(i, j).setBackground(QtGui.QColor(187, 134, 192, 50))
self.tableWidget.item(i, j).setToolTip('Droit insuffisant pour ouvrire la couche !') self.tableWidget.item(i, j).setToolTip('Droit insuffisant pour ouvrire la couche !')
@ -478,20 +427,6 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
cur.execute(SQLProjectsQgis) cur.execute(SQLProjectsQgis)
list_projects_qgis = cur.fetchall() list_projects_qgis = cur.fetchall()
for Project in list_projects_qgis: for Project in list_projects_qgis:
FavStatut = 0
if (ProjectName[0] + '.' + Project[0]) not in FavList:
FAV = "mStarIconAdd.png"
FavStatut = 0
else:
FAV = "mStarIconDel.png"
FavStatut = 1
iconFav = QIcon()
iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1))
self.FavButton = QPushButton(iconFav, "")
self.FavButton.clicked.connect(self.AddOrDelToUserFav)
if dbtype == refdb:
self.FavButton.setEnabled(False)
self.tableWidget.setRowCount(i + 1) self.tableWidget.setRowCount(i + 1)
itemIcon = QTableWidgetItem() itemIcon = QTableWidgetItem()
icon = QIcon() icon = QIcon()
@ -508,9 +443,8 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
item = QTableWidgetItem(Project[0]) item = QTableWidgetItem(Project[0])
self.tableWidget.setItem(i, 3, item) self.tableWidget.setItem(i, 3, item)
item = QTableWidgetItem(str(FavStatut)) item = QTableWidgetItem('FavIcon')
self.tableWidget.setItem(i, 4, item) self.tableWidget.setItem(i, 4, item)
self.tableWidget.setCellWidget(i, 4, self.FavButton)
i = i + 1 i = i + 1
@ -525,10 +459,11 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.filtre_dynamique(self.lineEdit.text()) self.filtre_dynamique(self.lineEdit.text())
def selection_flux(self): def selection_flux(self):
"""Ajoute une couche sélectionnée à la liste des flux à charger"""
selected_row = 0 selected_row = 0
selected_items = self.tableWidget.selectedItems() selected_items = self.tableWidget.selectedItems()
# Assuming you want to compare items in the first column for uniqueness
# svgTake = (selected_items[2].tableWidget().cellWidget(0,0))
new_item_text = selected_items[3].text() new_item_text = selected_items[3].text()
if not self.item_already_exists(new_item_text): if not self.item_already_exists(new_item_text):
@ -545,7 +480,6 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.tableWidget_2.setColumnWidth(2, 300) self.tableWidget_2.setColumnWidth(2, 300)
self.tableWidget_2.setColumnWidth(3, 300) self.tableWidget_2.setColumnWidth(3, 300)
self.tableWidget_2.setColumnWidth(4, 20) self.tableWidget_2.setColumnWidth(4, 20)
self.tableWidget_2.setColumnHidden(4, True)
def item_already_exists(self, new_item_text): def item_already_exists(self, new_item_text):
# Assuming you want to compare items in the first column for uniqueness # Assuming you want to compare items in the first column for uniqueness
@ -554,8 +488,7 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
return len(existing_items) > 0 return len(existing_items) > 0
def limite_flux(self): def limite_flux(self):
"""Vérifie le nombre de flux à charger et affiche un avertissement si > 5"""
# Avertissement si plus de 5 couches sélectionnées (risque de performance)
if self.tableWidget_2.rowCount() > 5: if self.tableWidget_2.rowCount() > 5:
self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !",
"Le nombre de flux à charger en une seule fois est limité à 5 pour des questions de performances. Souhaitez vous tout de même charger les " + str( "Le nombre de flux à charger en une seule fois est limité à 5 pour des questions de performances. Souhaitez vous tout de même charger les " + str(
@ -570,8 +503,26 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
if self.tableWidget_2.rowCount() <= 5: if self.tableWidget_2.rowCount() <= 5:
self.chargement_flux() self.chargement_flux()
"""
def openPostGIS(self, schema, table):
uri = QgsDataSourceUri()
uri.setConnection(host, port, sigdb, user, mdp)
if (schema + '.' + table) in LRasterList:
uri.setDataSource(schema, table, "rast")
uri.setKeyColumn('rid')
uri.setSrid('2154')
layer = QgsRasterLayer(uri.uri(), table, "postgresraster")
else:
uri.setDataSource(schema, table, "geom")
uri.setKeyColumn('gid')
uri.setSrid('2154')
layer = QgsVectorLayer(uri.uri(), table, "postgres")
# Ajout de la couche au canevas QGIS
QgsProject.instance().addMapLayer(layer)
"""
def chargement_flux(self): def chargement_flux(self):
"""Charge les couches sélectionnées dans QGIS"""
managerAU = QgsApplication.authManager() managerAU = QgsApplication.authManager()
managerAU.availableAuthMethodConfigs().keys() managerAU.availableAuthMethodConfigs().keys()
@ -585,7 +536,9 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
} }
return switcher.get(type, "nothing") return switcher.get(type, "nothing")
# Vérification de la présence de l'extension PostGIS Raster # def displayOnWindows(type, uri, name):
# p = []
SQLloadRaster = """SELECT concat(r_table_schema,'.',r_table_name) from public.raster_columns; """ SQLloadRaster = """SELECT concat(r_table_schema,'.',r_table_name) from public.raster_columns; """
SQLextension = """SELECT count(extname) FROM pg_catalog.pg_extension WHERE extname LIKE 'postgis_raster';""" SQLextension = """SELECT count(extname) FROM pg_catalog.pg_extension WHERE extname LIKE 'postgis_raster';"""
@ -602,10 +555,10 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
for rasterFind in list_raster: for rasterFind in list_raster:
RasterList.append(rasterFind[0]) RasterList.append(rasterFind[0])
# Chargement de chaque couche sélectionnée
for row in range(0, self.tableWidget_2.rowCount()): for row in range(0, self.tableWidget_2.rowCount()):
color_rgba_db = 855030089 # Code couleur pour couche dans autre BD color_rgba_db = 855030089
color_rgba_droit = 851150528 # Code couleur pour droits insuffisants color_rgba_droit = 851150528
print(self.tableWidget_2.item(row, 1).background().color().rgba())
if self.tableWidget_2.item(row, 1).background().color().rgba() == color_rgba_droit: if self.tableWidget_2.item(row, 1).background().color().rgba() == color_rgba_droit:
self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", "Vous ne disposez pas des droit pour la couche «" + str(self.tableWidget_2.item(row, 1).text()) + ' ' + str(self.tableWidget_2.item(row, 2).text()) + "» !", QMessageBox.StandardButton(0x00004000)) self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", "Vous ne disposez pas des droit pour la couche «" + str(self.tableWidget_2.item(row, 1).text()) + ' ' + str(self.tableWidget_2.item(row, 2).text()) + "» !", QMessageBox.StandardButton(0x00004000))
elif self.tableWidget_2.item(row, 1).background().color().rgba() != color_rgba_db: elif self.tableWidget_2.item(row, 1).background().color().rgba() != color_rgba_db:
@ -625,30 +578,27 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
code = self.tableWidget_2.item(row, 1).text() code = self.tableWidget_2.item(row, 1).text()
schema = self.tableWidget_2.item(row, 2).text() schema = self.tableWidget_2.item(row, 2).text()
table = self.tableWidget_2.item(row, 3).text() table = self.tableWidget_2.item(row, 3).text()
# Configuration de l'URI de connexion PostGIS # .split("?", 1)[0]
uri = QgsDataSourceUri() uri = QgsDataSourceUri()
uri.setConnection(host, port, dbtype, user, mdp) uri.setConnection(host, port, dbtype, user, mdp)
# nom du schéma à remplacer: "hydrographie" à supprimer et mettre "couches_collaboratives" lorsqu'on aura regroupé les couches à modifier dans un même
# Chargement selon le type de couche (raster, vecteur ou projet QGIS)
if (schema + '.' + table) in RasterList: if (schema + '.' + table) in RasterList:
# Chargement d'une couche raster
uri.setDataSource(schema, table, "rast") uri.setDataSource(schema, table, "rast")
uri.setKeyColumn('rid') # Clé primaire pour les rasters uri.setKeyColumn('rid')
uri.setSrid('2154') # Lambert 93 uri.setSrid('2154')
layer = QgsRasterLayer(uri.uri(), table, "postgresraster") layer = QgsRasterLayer(uri.uri(), table, "postgresraster")
QgsProject.instance().addMapLayer(layer) QgsProject.instance().addMapLayer(layer)
elif code == 'qgis': elif code == 'qgis':
# Chargement d'un projet QGIS stocké dans la base de données
schema = self.tableWidget_2.item(row, 2).text() schema = self.tableWidget_2.item(row, 2).text()
print(schema)
table = self.tableWidget_2.item(row, 3).text() table = self.tableWidget_2.item(row, 3).text()
uri_project = 'postgresql://' + user + ':' + mdp + '@' + host + ':' + port + '?sslmode=disable&dbname=' + dbtype + "&schema=" + schema + '&project=' + table uri_project = 'postgresql://' + user + ':' + mdp + '@' + host + ':' + port + '?sslmode=disable&dbname=' + dbtype + "&schema=" + schema + '&project=' + table
QgsProject.instance().read(uri_project) QgsProject.instance().read(uri_project)
else: else:
# Chargement d'une couche vecteur uri.setDataSource(schema, table, "geom")
uri.setDataSource(schema, table, "geom") # Colonne géométrie uri.setKeyColumn('gid')
uri.setKeyColumn('gid') # Clé primaire
# Détection du type de géométrie # Chargement de la couche PostGIS
geom_type = 'SELECT right(st_geometrytype(geom),-3) as a FROM ' + schema + '.' + table + ' GROUP BY a' geom_type = 'SELECT right(st_geometrytype(geom),-3) as a FROM ' + schema + '.' + table + ' GROUP BY a'
cur.execute(geom_type) cur.execute(geom_type)
list_typegeom = cur.fetchall() list_typegeom = cur.fetchall()
@ -679,9 +629,9 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", "La couche «" + str(self.tableWidget_2.item(row, 1).text()) + ' ' + str(self.tableWidget_2.item(row, 2).text()) + "» ne ce trouve pas dans cette BD !", QMessageBox.StandardButton(0x00004000)) self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", "La couche «" + str(self.tableWidget_2.item(row, 1).text()) + ' ' + str(self.tableWidget_2.item(row, 2).text()) + "» ne ce trouve pas dans cette BD !", QMessageBox.StandardButton(0x00004000))
def combobox_custom(self): def combobox_custom(self):
"""Remplit la liste déroulante des catégories selon la base de données sélectionnée""" # print('Chargement de la list pour la BD: ' + dbtype)
global NoSignals global NoSignals
NoSignals = 1 # Désactive temporairement les signaux pour éviter les boucles NoSignals = 1
if dbtype == sigdb: if dbtype == sigdb:
self.toolButton.setEnabled(1) self.toolButton.setEnabled(1)
self.comboBox.clear() self.comboBox.clear()
@ -708,29 +658,18 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
NoSignals = 0 NoSignals = 0
def filtre_favorit(self, filter_fav): def filtre_favorit(self, filter_fav):
"""Active ou désactive le filtre des favoris""" if self.toolButton_2.text() == "0":
if self.toolButton_2.text() == "0": # Activation du filtre
self.toolButton_2.setText("1") self.toolButton_2.setText("1")
self.toolButton_2.setDown(True) for i in range(self.tableWidget.rowCount()):
if self.lineEdit.text() != 'Recherche par mots-clés': item = self.tableWidget.item(i, 4)
self.filtre_dynamique(self.lineEdit.text()) match = self.toolButton_2.text() not in item.text().lower()
else: self.tableWidget.setRowHidden(i, match)
for i in range(self.tableWidget.rowCount()):
item = self.tableWidget.item(i, 4)
match = self.toolButton_2.text() not in item.text().lower()
self.tableWidget.setRowHidden(i, match)
elif self.toolButton_2.text() == "1": elif self.toolButton_2.text() == "1":
self.toolButton_2.setText("0") self.toolButton_2.setText("0")
self.toolButton_2.setDown(False) for i in range(self.tableWidget.rowCount()):
if self.lineEdit.text() != 'Recherche par mots-clés': self.tableWidget.setRowHidden(i, False)
self.filtre_dynamique(self.lineEdit.text())
else:
for i in range(self.tableWidget.rowCount()):
self.tableWidget.setRowHidden(i, False)
def filtre_dynamique(self, filter_text): def filtre_dynamique(self, filter_text):
"""Filtre dynamique des couches selon le texte saisi"""
# Remplacement des espaces par des underscores pour la recherche
if filter_text.find(' ') >= 0: if filter_text.find(' ') >= 0:
filter_text = filter_text.replace(" ", "_") filter_text = filter_text.replace(" ", "_")
for i in range(self.tableWidget.rowCount()): for i in range(self.tableWidget.rowCount()):
@ -746,24 +685,6 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
break break
def getCanevas(self): def getCanevas(self):
"""Filtre les couches selon l'emprise du canevas QGIS actuel"""
# Récupération de la liste des favoris de l'utilisateur
SQLFavTable = "SELECT concat(schema_name, '.', table_name) FROM admin_sig.favtable WHERE utilisateur LIKE '" + user + "';"
if dbtype == refdb:
conSIG = psycopg2.connect("host=" + host + " port=" + port + " dbname=" + sigdb + " user=" + user + " password=" + mdp)
curSIG = conSIG.cursor(cursor_factory=psycopg2.extras.DictCursor)
curSIG.execute(SQLFavTable)
list_fav = curSIG.fetchall()
conSIG.close()
else:
cur.execute(SQLFavTable)
list_fav = cur.fetchall()
FavList = []
for favFind in list_fav:
FavList.append(favFind[0])
# Récupération de l'emprise du canevas
poly = iface.mapCanvas().extent() poly = iface.mapCanvas().extent()
geom = (str(poly.xMinimum()) + ',' + str(poly.yMinimum()) + ',' + str(poly.xMaximum()) + ',' + str(poly.yMaximum())) geom = (str(poly.xMinimum()) + ',' + str(poly.yMinimum()) + ',' + str(poly.xMaximum()) + ',' + str(poly.yMaximum()))
@ -795,28 +716,13 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
TableSomme = cur.fetchall()[0][0] TableSomme = cur.fetchall()[0][0]
if e[0][1:3] != 'fo': if e[0][1:3] != 'fo':
schema_name = e[0][4:]
DepName = QTableWidgetItem(e[0][1:3]) DepName = QTableWidgetItem(e[0][1:3])
SchemaName = QTableWidgetItem(schema_name) SchemaName = QTableWidgetItem(e[0][4:])
else: else:
schema_name = e[0][6:]
DepName = QTableWidgetItem('form') DepName = QTableWidgetItem('form')
SchemaName = QTableWidgetItem(schema_name) SchemaName = QTableWidgetItem(e[0][6:])
table_name = e[1][len(e[0]) + 1:] TableName = QTableWidgetItem(e[1][len(e[0]) + 1:])
TableName = QTableWidgetItem(table_name) FavoritStatut = QTableWidgetItem('FavIcon')
FavStatut = 0
if (schema_name + '.' + table_name) not in FavList:
FAV = "mStarIconAdd.png"
FavStatut = 0
else:
FAV = "mStarIconDel.png"
FavStatut = 1
iconFav = QIcon()
iconFav.addPixmap(QtGui.QPixmap(resources_path('icons', FAV)), QIcon.Mode(0), QIcon.State(1))
self.FavButton = QPushButton(iconFav, "")
self.FavButton.clicked.connect(self.AddOrDelToUserFav)
self.tableWidget.insertRow(row_count) self.tableWidget.insertRow(row_count)
itemIcon = QTableWidgetItem() itemIcon = QTableWidgetItem()
@ -828,9 +734,7 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
self.tableWidget.setItem(row_count, 1, DepName) self.tableWidget.setItem(row_count, 1, DepName)
self.tableWidget.setItem(row_count, 2, SchemaName) self.tableWidget.setItem(row_count, 2, SchemaName)
self.tableWidget.setItem(row_count, 3, TableName) self.tableWidget.setItem(row_count, 3, TableName)
item = QTableWidgetItem(str(FavStatut)) self.tableWidget.setItem(row_count, 3, FavoritStatut)
self.tableWidget.setItem(row_count, 4, item)
self.tableWidget.setCellWidget(row_count, 4, self.FavButton)
if TableSomme == 0: if TableSomme == 0:
for j in range(self.tableWidget.columnCount()): for j in range(self.tableWidget.columnCount()):
self.tableWidget.item(row_count, j).setBackground(QtGui.QColor(246, 185, 73, 50)) self.tableWidget.item(row_count, j).setBackground(QtGui.QColor(246, 185, 73, 50))
@ -846,3 +750,7 @@ class Flux_Editor(QDialog, EDITOR_CLASS):
new_object = new_object + '[' + str(obj_X[1]) + ',' + str(obj_X[0]) + '],' new_object = new_object + '[' + str(obj_X[1]) + ',' + str(obj_X[0]) + '],'
new_object = new_object[:-1] + ']' new_object = new_object[:-1] + ']'
return (new_object) return (new_object)
# def popup(self):
# self.dialog = Popup() # +++ - self
# self.dialog.text_edit.show()

View File

@ -7,7 +7,7 @@ name=CenRa_FLUX
qgisMinimumVersion=3.0 qgisMinimumVersion=3.0
supportsQt6=True supportsQt6=True
description=Permet d'ouvrire une table dans la base PostGis description=Permet d'ouvrire une table dans la base PostGis
version=3.20 version=3.15
author=Conservatoire d'Espaces Naturels de Rhône-Alpes author=Conservatoire d'Espaces Naturels de Rhône-Alpes
email=si_besoin@cen-rhonealpes.fr email=si_besoin@cen-rhonealpes.fr
@ -32,7 +32,7 @@ icon=icon.png
# experimental flag # experimental flag
experimental=False experimental=False
changelog=<h2>CenRa_FLUX:</h2></br><p><h3>05/03/2026 - Version 3.20: </h3> - Visualisation des metadonnees avec un tooltip au survol de la couche.</p></br><p><h3>26/02/2026 - Version 3.19: </h3> - Visualisation des droits pour les vues materialisees.</p></br><p><h3>25/02/2026 - Version 3.18: </h3> - Filtre par secteur geographique compatible avec les favoris.</p></br><p><h3>12/02/2026 - Version 3.17: </h3> - Favorit dans la BD Ref.</p></br><p><h3>11/02/2026 - Version 3.16: </h3> - Creation de favori et visualisation.</p></br><p><h3>10/02/2026 - Version 3.15: </h3> - Sauvegarde la recherche.</p></br><p><h3>08/12/2025 - Version 3.14: </h3> - message d'erreur pour les drois de couche sur la DB.</p></br><p><h3>08/12/2025 - Version 3.13: </h3> - Detection des droit utilisateur.</p></br><p><h3>25/09/2025 - Version 3.12: </h3> - version +1.</p></br><p><h3>25/09/2025 - Version 3.11: </h3> - Correctife sur les code 00.</p></br><p><h3>24/09/2025 - Version 3.10: </h3> - Erreur sur l ouverture des couche raster. </p></br><p><h3>24/09/2025 - Version 3.9: </h3> - bugfix lier aux extention pgsql.</p></br><p><h3>09/09/2025 - Version 3.8: </h3> - Bug REF fix.</p></br><p><h3>05/09/2025 - Version 3.7: </h3> - Ouverture de projet QGIS contenue dans la base de donnees.</p></br><p><h3>30/07/2025 - Version 3.6: </h3> - Correctife de bug.</p></br><p><h3>29/07/2025 - Version 3.5: </h3> - Bug fix sur les donnee raster.</p></br><p><h3>23/07/2025 - Version 3.4: </h3> - Ouverture raster dans la base SIG.</p></br><p><h3>23/07/2025 - Version 3.3: </h3> - Optimisation des chargement.</p></br><p><h3>22/07/2025 - Version 3.2: </h3> - Visualisation des format raster et vecteur dans REF.</p></br><p><h3>21/07/2025 - Version 3.1: </h3> - Bug fix pour l'ouverture de plus de 5 couches.</p></br><p><h3>19/05/2025 - Version 3.0: </h3> - Compatible PyQt5 et PyQt6.</p></br><p><h3>09/04/2025 - Version 2.9: </h3> - Correctif bug en TT.</p></br><p><h3>09/04/2025 - Version 2.8: </h3> - Optimisation pour le TT.</p></br><p><h3>07/04/2025 - Version 2.7: </h3> - mode debug.</p></br><p><h3>03/04/2025 - Version 2.6: </h3> - Mise a jour de securite.</p></br><p><h3>20/03/2025 - Version 2.5: </h3> - Visualisation distincte des couches ne se trouvant pas dans l'antenne.</p></br><p><h3>13/02/2025 - Version 2.4: </h3> - Ajoute redimensionnement et déplacement mollette.</p></br><p><h3>05/02/2025 - Version 2.3: </h3> - Bouton de visualisation des couches se trouvent uniquement dans le canva de la carte.</p></br><p><h3>07/01/2025 - Version 2.2: </h3> - ByPass du certif ssl ci erreur.</p></br><p><h3>22/10/2024 - Version 2.1:</h3> - Correctif de bug.</br> - Evolution de la limit de 3 à 5. </br></p></br><p><h3>22/10/2024 - Version 2.0:</h3> - Reformatage du code.</br></p></br><p><h3>03/10/2024 - Version 1.14:</h3> - Remonte la fênetre dans la pille.</br></p><p><h3>13/09/2024 - Version 1.13:</h3>- MAJ sur le lien du changelog</br>- Bug-fix: Ouvre MultiPolygone et Polygon séparément.</p></br><p><h3>10/09/2024 - Version 1.11:</h3>- Ouverture de table contenant plusieurs géométries.</p></br><p><h3>26/08/2024 - Version 1.10:</h3>- Ajoute d'un changelog et vérification de mise à jour.</p> changelog=<h2>CenRa_FLUX:</h2></br><p><h3>10/02/2026 - Version 3.15: </h3> - Sauvegarde la recherche.</p></br><p><h3>08/12/2025 - Version 3.14: </h3> - message d'erreur pour les drois de couche sur la DB.</p></br><p><h3>08/12/2025 - Version 3.13: </h3> - Detection des droit utilisateur.</p></br><p><h3>25/09/2025 - Version 3.12: </h3> - version +1.</p></br><p><h3>25/09/2025 - Version 3.11: </h3> - Correctife sur les code 00.</p></br><p><h3>24/09/2025 - Version 3.10: </h3> - Erreur sur l ouverture des couche raster. </p></br><p><h3>24/09/2025 - Version 3.9: </h3> - bugfix lier aux extention pgsql.</p></br><p><h3>09/09/2025 - Version 3.8: </h3> - Bug REF fix.</p></br><p><h3>05/09/2025 - Version 3.7: </h3> - Ouverture de projet QGIS contenue dans la base de donnees.</p></br><p><h3>30/07/2025 - Version 3.6: </h3> - Correctife de bug.</p></br><p><h3>29/07/2025 - Version 3.5: </h3> - Bug fix sur les donnee raster.</p></br><p><h3>23/07/2025 - Version 3.4: </h3> - Ouverture raster dans la base SIG.</p></br><p><h3>23/07/2025 - Version 3.3: </h3> - Optimisation des chargement.</p></br><p><h3>22/07/2025 - Version 3.2: </h3> - Visualisation des format raster et vecteur dans REF.</p></br><p><h3>21/07/2025 - Version 3.1: </h3> - Bug fix pour l'ouverture de plus de 5 couches.</p></br><p><h3>19/05/2025 - Version 3.0: </h3> - Compatible PyQt5 et PyQt6.</p></br><p><h3>09/04/2025 - Version 2.9: </h3> - Correctif bug en TT.</p></br><p><h3>09/04/2025 - Version 2.8: </h3> - Optimisation pour le TT.</p></br><p><h3>07/04/2025 - Version 2.7: </h3> - mode debug.</p></br><p><h3>03/04/2025 - Version 2.6: </h3> - Mise a jour de securite.</p></br><p><h3>20/03/2025 - Version 2.5: </h3> - Visualisation distincte des couches ne se trouvant pas dans l'antenne.</p></br><p><h3>13/02/2025 - Version 2.4: </h3> - Ajoute redimensionnement et déplacement mollette.</p></br><p><h3>05/02/2025 - Version 2.3: </h3> - Bouton de visualisation des couches se trouvent uniquement dans le canva de la carte.</p></br><p><h3>07/01/2025 - Version 2.2: </h3> - ByPass du certif ssl ci erreur.</p></br><p><h3>22/10/2024 - Version 2.1:</h3> - Correctif de bug.</br> - Evolution de la limit de 3 à 5. </br></p></br><p><h3>22/10/2024 - Version 2.0:</h3> - Reformatage du code.</br></p></br><p><h3>03/10/2024 - Version 1.14:</h3> - Remonte la fênetre dans la pille.</br></p><p><h3>13/09/2024 - Version 1.13:</h3>- MAJ sur le lien du changelog</br>- Bug-fix: Ouvre MultiPolygone et Polygon séparément.</p></br><p><h3>10/09/2024 - Version 1.11:</h3>- Ouverture de table contenant plusieurs géométries.</p></br><p><h3>26/08/2024 - Version 1.10:</h3>- Ajoute d'un changelog et vérification de mise à jour.</p>
# deprecated flag (applies to the whole plugin, not just a single version) # deprecated flag (applies to the whole plugin, not just a single version)
deprecated=False deprecated=False

View File

@ -2,57 +2,13 @@
schemaname_distinct = """SELECT DISTINCT schemaname from pg_catalog.pg_tables schemaname_distinct = """SELECT DISTINCT schemaname from pg_catalog.pg_tables
WHERE schemaname NOT LIKE '_archives' AND schemaname NOT LIKE 'topology' AND schemaname NOT LIKE 'information_schema' AND schemaname NOT LIKE 'pg_catalog' and schemaname NOT LIKE 'public' AND schemaname NOT LIKE '_trier' WHERE schemaname NOT LIKE '_archives' AND schemaname NOT LIKE 'topology' AND schemaname NOT LIKE 'information_schema' AND schemaname NOT LIKE 'pg_catalog' and schemaname NOT LIKE 'public' AND schemaname NOT LIKE '_trier'
order by schemaname;""" order by schemaname;"""
schemaname_list_ref = """SELECT schemaname,tablename from pg_catalog.pg_tables
schemaname_list_ref = """ WHERE schemaname NOT LIKE '_archives' AND schemaname NOT LIKE 'topology' AND schemaname NOT LIKE 'information_schema' AND schemaname NOT LIKE 'pg_catalog' and schemaname NOT LIKE 'public' AND schemaname NOT LIKE '_trier'
WITH TableList AS (SELECT order by schemaname,tablename;"""
schemaname, schemaname_list = """(SELECT schemaname,tablename from pg_catalog.pg_tables
tablename where schemaname like 'trav%' or schemaname like '\\_ag%' or schemaname like '\\_00%' or schemaname like '\\_01%' or schemaname like '\\_07%' or schemaname like '\\_26%' or schemaname like '\\_form%' or schemaname like '\\_42%' or schemaname like '\\_69%' order by schemaname,tablename)
FROM pg_catalog.pg_tables UNION
WHERE (SELECT schemaname,matviewname AS tablename FROM pg_catalog.pg_matviews order by schemaname,tablename) order by schemaname,tablename;"""
schemaname NOT LIKE '_archives' AND schemaname NOT LIKE 'topology' AND schemaname NOT LIKE 'information_schema' AND schemaname NOT LIKE 'pg_catalog' and schemaname NOT LIKE 'public' AND schemaname NOT LIKE '_trier'
ORDER BY schemaname,tablename),
TableDescription AS (SELECT
schemaname,
tablename,
description
FROM TableList
LEFT JOIN pg_description ON objoid = (quote_ident(schemaname)||'.'||quote_ident(tablename))::regclass
ORDER BY schemaname, tablename)
SELECT * FROM TableDescription;
"""
schemaname_list = """
WITH
TableList as (SELECT
schemaname,
tablename
FROM pg_catalog.pg_tables
WHERE schemaname LIKE 'trav%' OR schemaname LIKE '_ag%' OR schemaname LIKE '_00%' OR schemaname LIKE '_01%' OR schemaname LIKE '_07%' OR schemaname LIKE '_26%' OR schemaname LIKE '_form%' OR schemaname LIKE '_42%' OR schemaname LIKE '_69%'
ORDER BY schemaname,tablename),
ViewList as (SELECT
schemaname,
matviewname AS tablename
FROM pg_catalog.pg_matviews
ORDER BY schemaname,tablename),
TableAndView as (SELECT
schemaname,
tablename
FROM TableList
UNION SELECT schemaname,tablename FROM ViewList),
TableViewDescription as (SELECT
schemaname,
tablename,
description
FROM TableAndView
LEFT JOIN pg_description ON objoid = (quote_ident(schemaname)||'.'||quote_ident(tablename))::regclass
ORDER BY schemaname,tablename)
SELECT * FROM TableViewDescription;
"""
geom = "geom" geom = "geom"
champ_travaux_prevus_multipolygon = """(gid serial NOT NULL, groupe_gestion text, gestion_lib text, id_gestion text, datedebut date, datefin date, commentaire text, surface_m2 double precision, surface_ha double precision, date_creation date, date_maj date, geom geometry(MultiPolygon,2154))""" champ_travaux_prevus_multipolygon = """(gid serial NOT NULL, groupe_gestion text, gestion_lib text, id_gestion text, datedebut date, datefin date, commentaire text, surface_m2 double precision, surface_ha double precision, date_creation date, date_maj date, geom geometry(MultiPolygon,2154))"""
champ_travaux_prevus_multilinestring = """(gid serial NOT NULL, groupe_gestion text, gestion_lib text, id_gestion text, datedebut date, datefin date, commentaire text, longueur_m double precision, longueur_km double precision, date_creation date, date_maj date, geom geometry(MultiLineString,2154))""" champ_travaux_prevus_multilinestring = """(gid serial NOT NULL, groupe_gestion text, gestion_lib text, id_gestion text, datedebut date, datefin date, commentaire text, longueur_m double precision, longueur_km double precision, date_creation date, date_maj date, geom geometry(MultiLineString,2154))"""

View File

@ -50,9 +50,9 @@
<tags>cenra,sicen</tags> <tags>cenra,sicen</tags>
</pyqgis_plugin> </pyqgis_plugin>
<pyqgis_plugin name="CenRa_FLUX" version="3.20"> <pyqgis_plugin name="CenRa_FLUX" version="3.15">
<description>Depot pour les extensiont QGIS du CEN Rhone-Alpes, sur GitHub.</description> <description>Depot pour les extensiont QGIS du CEN Rhone-Alpes, sur GitHub.</description>
<version>3.20</version> <version>3.15</version>
<qgis_minimum_version>3.16</qgis_minimum_version> <qgis_minimum_version>3.16</qgis_minimum_version>
<homepage>https://plateformesig.cenra-outils.org/</homepage> <homepage>https://plateformesig.cenra-outils.org/</homepage>
<file_name>CenRa_FLUX.zip</file_name> <file_name>CenRa_FLUX.zip</file_name>
@ -61,7 +61,7 @@
<download_url>https://gitea.cenra-outils.org/CEN-RA/Plugin_QGIS/releases/download/latest/CenRa_FLUX.zip</download_url> <download_url>https://gitea.cenra-outils.org/CEN-RA/Plugin_QGIS/releases/download/latest/CenRa_FLUX.zip</download_url>
<uploaded_by>CEN-Rhone-Alpes</uploaded_by> <uploaded_by>CEN-Rhone-Alpes</uploaded_by>
<create_date>2024-02-06</create_date> <create_date>2024-02-06</create_date>
<update_date>2026-03-05</update_date> <update_date>2026-02-10</update_date>
<experimental>False</experimental> <experimental>False</experimental>
<deprecated>False</deprecated> <deprecated>False</deprecated>
<tags>cenra,flux</tags> <tags>cenra,flux</tags>