diff --git a/.ci/ctest2ci.py b/.ci/ctest2ci.py
index 9a6ee626ef7a..4ccd9f94e1b6 100755
--- a/.ci/ctest2ci.py
+++ b/.ci/ctest2ci.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
"""
***************************************************************************
@@ -18,42 +17,47 @@
***************************************************************************
"""
-__author__ = 'Matthias Kuhn'
-__date__ = 'March 2017'
-__copyright__ = '(C) 2017, Matthias Kuhn'
+__author__ = "Matthias Kuhn"
+__date__ = "March 2017"
+__copyright__ = "(C) 2017, Matthias Kuhn"
# This script parses output from ctest and injects
#
# - Colors for failing unit tests and test cases
# - Group control sequences to hide uninteresting output by default
-import sys
import re
+import string
import subprocess
+import sys
+
from termcolor import colored
-import string
fold_stack = list()
printable = set(string.printable)
def start_fold(tag):
- sys.stdout.write('::group::{}\n'.format(tag))
+ sys.stdout.write(f"::group::{tag}\n")
fold_stack.append(tag)
def end_fold():
try:
tag = fold_stack.pop()
- sys.stdout.write('::endgroup::\n')
+ sys.stdout.write("::endgroup::\n")
except IndexError:
- updated_line = colored("======================", 'magenta')
- updated_line += colored("ctest2ci error when processing the following line:", 'magenta')
- updated_line += colored("----------------------", 'magenta')
- updated_line += colored(updated_line, 'magenta')
- updated_line += colored("----------------------", 'magenta')
- updated_line += colored("Tried to end fold, but fold was never started.", 'magenta')
- updated_line += colored("======================", 'magenta')
+ updated_line = colored("======================", "magenta")
+ updated_line += colored(
+ "ctest2ci error when processing the following line:", "magenta"
+ )
+ updated_line += colored("----------------------", "magenta")
+ updated_line += colored(updated_line, "magenta")
+ updated_line += colored("----------------------", "magenta")
+ updated_line += colored(
+ "Tried to end fold, but fold was never started.", "magenta"
+ )
+ updated_line += colored("======================", "magenta")
test_count = 0
@@ -61,8 +65,8 @@ def end_fold():
def start_test_fold():
global test_count
- sys.stdout.write('Running tests\n')
- start_fold('test.{}'.format(test_count))
+ sys.stdout.write("Running tests\n")
+ start_fold(f"test.{test_count}")
test_count += 1
@@ -72,55 +76,61 @@ def start_test_fold():
p = subprocess.Popen(sys.argv[1:], stdout=subprocess.PIPE)
for line in p.stdout:
- updated_line = line.decode('utf-8')
+ updated_line = line.decode("utf-8")
# remove non printable characters https://stackoverflow.com/a/8689826/1548052
filter(lambda x: x in printable, updated_line)
- if re.match('Run dashboard with model Experimental', updated_line):
- start_fold('Run tests')
- updated_line = '{title}\n{line}'.format(title=colored('Running tests...', 'yellow', attrs=['bold']),
- line=updated_line)
-
- elif re.match('Test project /home/runner/QGIS/QGIS/build', updated_line):
+ if re.match("Run dashboard with model Experimental", updated_line):
+ start_fold("Run tests")
+ updated_line = "{title}\n{line}".format(
+ title=colored("Running tests...", "yellow", attrs=["bold"]),
+ line=updated_line,
+ )
+
+ elif re.match("Test project /home/runner/QGIS/QGIS/build", updated_line):
end_fold() # tag=Run tests
start_test_fold()
- if re.search(r'\*\*\*Failed', updated_line) or re.search(r'\*\*\*Timeout', updated_line):
+ if re.search(r"\*\*\*Failed", updated_line) or re.search(
+ r"\*\*\*Timeout", updated_line
+ ):
end_fold()
- updated_line = colored(updated_line, 'red')
+ updated_line = colored(updated_line, "red")
in_failing_test = True
if in_failing_test:
- if re.match(' Start', updated_line):
+ if re.match(" Start", updated_line):
start_test_fold()
in_failing_test = False
elif in_failure:
- if re.match('PASS', updated_line) or re.match('Ran', updated_line):
+ if re.match("PASS", updated_line) or re.match("Ran", updated_line):
in_failure = False
else:
- updated_line = colored(updated_line, 'yellow')
- elif re.search(r'\*\*\* Segmentation fault', updated_line):
- start_fold('segfault')
- updated_line = colored(updated_line, 'magenta')
- elif re.match(' Test failed: Segmentation fault', updated_line):
+ updated_line = colored(updated_line, "yellow")
+ elif re.search(r"\*\*\* Segmentation fault", updated_line):
+ start_fold("segfault")
+ updated_line = colored(updated_line, "magenta")
+ elif re.match(" Test failed: Segmentation fault", updated_line):
end_fold()
else:
- if re.match(r'(FAIL|ERROR)[:\!].*', updated_line):
- updated_line = colored(updated_line, 'yellow')
+ if re.match(r"(FAIL|ERROR)[:\!].*", updated_line):
+ updated_line = colored(updated_line, "yellow")
in_failure = True
- if not in_failing_test and re.search('[0-9]+% tests passed, [0-9]+ tests failed out of', updated_line):
- tests_failing = re.match(r'.* ([0-9]+) tests failed', updated_line).group(1)
+ if not in_failing_test and re.search(
+ "[0-9]+% tests passed, [0-9]+ tests failed out of", updated_line
+ ):
+ tests_failing = re.match(r".* ([0-9]+) tests failed", updated_line).group(1)
# updated_line += '\n::set-output name=TESTS_FAILING::{}'.format(tests_failing)
end_fold()
- if re.search('100% tests passed', updated_line):
- updated_line = colored(updated_line, 'green')
+ if re.search("100% tests passed", updated_line):
+ updated_line = colored(updated_line, "green")
- if re.match('Submit files', updated_line):
- start_fold('submit')
- elif re.search('Test results submitted to', updated_line):
- cdash_url = re.match(r'.*(http.*)$', updated_line).group(1)
+ if re.match("Submit files", updated_line):
+ start_fold("submit")
+ elif re.search("Test results submitted to", updated_line):
+ cdash_url = re.match(r".*(http.*)$", updated_line).group(1)
# updated_line += '\n::set-output name=CDASH_URL::{}'.format(cdash_url)
end_fold()
diff --git a/.ci/pr_has_label.py b/.ci/pr_has_label.py
index c2475e8d2620..d2a8aca7a98f 100755
--- a/.ci/pr_has_label.py
+++ b/.ci/pr_has_label.py
@@ -1,34 +1,37 @@
#!/usr/bin/env python3
-import sys
+import argparse
import json
-from urllib.request import urlopen # using urllib since it is a standard module (vs. requests)
+import sys
+
from urllib.error import URLError
-import argparse
+from urllib.request import ( # using urllib since it is a standard module (vs. requests)
+ urlopen,
+)
-parser = argparse.ArgumentParser(description='Determines if a pull request has a defined label')
-parser.add_argument('pull_request', type=str,
- help='pull request id')
-parser.add_argument('label', type=int,
- help='label ID')
+parser = argparse.ArgumentParser(
+ description="Determines if a pull request has a defined label"
+)
+parser.add_argument("pull_request", type=str, help="pull request id")
+parser.add_argument("label", type=int, help="label ID")
args = parser.parse_args()
-if args.pull_request == 'false':
+if args.pull_request == "false":
print("false")
sys.exit(1)
-url = "https://api.github.com/repos/qgis/QGIS/pulls/{}".format(args.pull_request)
+url = f"https://api.github.com/repos/qgis/QGIS/pulls/{args.pull_request}"
try:
- data = urlopen(url).read().decode('utf-8')
+ data = urlopen(url).read().decode("utf-8")
except URLError as err:
- print("URLError: {}".format(err.reason))
+ print(f"URLError: {err.reason}")
sys.exit(1)
obj = json.loads(data)
-for label in obj['labels']:
+for label in obj["labels"]:
if label["id"] == args.label:
print("true")
sys.exit(0)
diff --git a/.docker/qgis_resources/test_runner/qgis_startup.py b/.docker/qgis_resources/test_runner/qgis_startup.py
index 94c07906f0e4..6475604e883a 100644
--- a/.docker/qgis_resources/test_runner/qgis_startup.py
+++ b/.docker/qgis_resources/test_runner/qgis_startup.py
@@ -6,16 +6,18 @@
~/.qgis3/python/startup.py
"""
-from qgis.core import Qgis
-from qgis import utils
+
import traceback
+from qgis import utils
+from qgis.core import Qgis
+
def _showException(type, value, tb, msg, messagebar=False, level=Qgis.Warning):
print(msg)
- logmessage = ''
+ logmessage = ""
for s in traceback.format_exception(type, value, tb):
- logmessage += s.decode('utf-8', 'replace') if hasattr(s, 'decode') else s
+ logmessage += s.decode("utf-8", "replace") if hasattr(s, "decode") else s
print(logmessage)
diff --git a/.docker/qgis_resources/test_runner/qgis_testrunner.py b/.docker/qgis_resources/test_runner/qgis_testrunner.py
index a04b9295ea35..7173a93c1da0 100755
--- a/.docker/qgis_resources/test_runner/qgis_testrunner.py
+++ b/.docker/qgis_resources/test_runner/qgis_testrunner.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
"""
***************************************************************************
@@ -42,18 +41,19 @@
***************************************************************************
"""
-__author__ = 'Alessandro Pasotti'
-__date__ = 'May 2016'
+__author__ = "Alessandro Pasotti"
+__date__ = "May 2016"
+import importlib
import os
import re
+import signal
import sys
import traceback
-import signal
-import importlib
-from pexpect import run
-from pipes import quote
+from shlex import quote
+
+from pexpect import run
from qgis.utils import iface
@@ -68,14 +68,17 @@ def __get_test_function(test_module_name):
print("QGIS Test Runner - Trying to import %s" % test_module_name)
try:
test_module = importlib.import_module(test_module_name)
- function_name = 'run_all'
+ function_name = "run_all"
except ImportError as e:
# traceback.print_exc(file=sys.stdout)
# Strip latest name
- pos = test_module_name.rfind('.')
+ pos = test_module_name.rfind(".")
if pos <= 0:
raise e
- test_module_name, function_name = test_module_name[:pos], test_module_name[pos + 1:]
+ test_module_name, function_name = (
+ test_module_name[:pos],
+ test_module_name[pos + 1 :],
+ )
print("QGIS Test Runner - Trying to import %s" % test_module_name)
sys.stdout.flush()
try:
@@ -93,47 +96,55 @@ def __get_test_function(test_module_name):
sys.path.append(os.getcwd())
test_module_name = sys.argv[-1]
if __get_test_function(test_module_name) is None:
- print("QGIS Test Runner - [ERROR] cannot load test function from %s" % test_module_name)
+ print(
+ "QGIS Test Runner - [ERROR] cannot load test function from %s"
+ % test_module_name
+ )
sys.exit(1)
try:
me = __file__
except NameError:
me = sys.argv[0]
- os.environ['QGIS_DEBUG'] = '1'
+ os.environ["QGIS_DEBUG"] = "1"
args = [
- 'qgis',
- os.environ.get('QGIS_EXTRA_OPTIONS', ''),
- '--nologo',
- '--noversioncheck',
- '--code',
+ "qgis",
+ os.environ.get("QGIS_EXTRA_OPTIONS", ""),
+ "--nologo",
+ "--noversioncheck",
+ "--code",
me,
test_module_name, # Must be the last one!
]
- command_line = ' '.join(args)
+ command_line = " ".join(args)
print("QGIS Test Runner - launching QGIS as %s ..." % command_line)
out, returncode = run("sh -c " + quote(command_line), withexitstatus=1)
if isinstance(out, bytes):
out = out.decode("utf-8")
assert returncode is not None
print("QGIS Test Runner - QGIS exited.")
- ok = out.find('(failures=') < 0 and \
- len(re.findall(r'Ran \d+ tests in\s',
- out, re.MULTILINE)) > 0
- print('=' * 60)
+ ok = (
+ out.find("(failures=") < 0
+ and len(re.findall(r"Ran \d+ tests in\s", out, re.MULTILINE)) > 0
+ )
+ print("=" * 60)
if not ok:
print(out)
else:
eprint(out)
if len(out) == 0:
print("QGIS Test Runner - [WARNING] subprocess returned no output")
- print('=' * 60)
+ print("=" * 60)
- print("QGIS Test Runner - %s bytes returned and finished with exit code: %s" % (len(out), 0 if ok else 1))
+ print(
+ "QGIS Test Runner - {} bytes returned and finished with exit code: {}".format(
+ len(out), 0 if ok else 1
+ )
+ )
sys.exit(0 if ok else 1)
else: # We are inside QGIS!
# Start as soon as the initializationCompleted signal is fired
- from qgis.core import QgsApplication, QgsProjectBadLayerHandler, QgsProject
+ from qgis.core import QgsApplication, QgsProject, QgsProjectBadLayerHandler
from qgis.PyQt.QtCore import QDir
from qgis.utils import iface
diff --git a/.github/actions/vcpkg_update_report/vcpkg-diff.py b/.github/actions/vcpkg_update_report/vcpkg-diff.py
index bc895b038b6c..21ff42ef772f 100644
--- a/.github/actions/vcpkg_update_report/vcpkg-diff.py
+++ b/.github/actions/vcpkg_update_report/vcpkg-diff.py
@@ -89,7 +89,7 @@ def read_file(file_path):
"""
Read the content of a file.
"""
- with open(file_path, "r") as file:
+ with open(file_path) as file:
return file.read()
diff --git a/cmake/FindPyQt5.py b/cmake/FindPyQt5.py
index fe029af9faca..39d3b246c43d 100644
--- a/cmake/FindPyQt5.py
+++ b/cmake/FindPyQt5.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (c) 2007, Simon Edwards
# All rights reserved.
@@ -31,16 +30,19 @@
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
import os.path
+import sys
+
import PyQt5.QtCore
import sipconfig
-import sys
cfg = sipconfig.Configuration()
sip_dir = cfg.default_sip_dir
-for p in (os.path.join(sip_dir, "PyQt5"),
- os.path.join(sip_dir, "PyQt5-3"),
- sip_dir,
- os.path.join(cfg.default_mod_dir, "PyQt5", "bindings")):
+for p in (
+ os.path.join(sip_dir, "PyQt5"),
+ os.path.join(sip_dir, "PyQt5-3"),
+ sip_dir,
+ os.path.join(cfg.default_mod_dir, "PyQt5", "bindings"),
+):
if os.path.exists(os.path.join(p, "QtCore", "QtCoremod.sip")):
sip_dir = p
break
@@ -48,7 +50,7 @@
print("pyqt_version_str:%s" % PyQt5.QtCore.PYQT_VERSION_STR)
print("pyqt_mod_dir:%s" % os.path.join(cfg.default_mod_dir, "PyQt5"))
print("pyqt_sip_dir:%s" % sip_dir)
-print("pyqt_sip_flags:%s" % PyQt5.QtCore.PYQT_CONFIGURATION['sip_flags'])
+print("pyqt_sip_flags:%s" % PyQt5.QtCore.PYQT_CONFIGURATION["sip_flags"])
print("pyqt_bin_dir:%s" % cfg.default_bin_dir)
try:
diff --git a/cmake/FindPyQt6.py b/cmake/FindPyQt6.py
index de735d467a18..8a149f4f1aba 100644
--- a/cmake/FindPyQt6.py
+++ b/cmake/FindPyQt6.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (c) 2007, Simon Edwards
# All rights reserved.
@@ -31,16 +30,19 @@
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
import os.path
+import sys
+
import PyQt6.QtCore
import sipconfig
-import sys
cfg = sipconfig.Configuration()
sip_dir = cfg.default_sip_dir
-for p in (os.path.join(sip_dir, "PyQt6"),
- os.path.join(sip_dir, "PyQt6-3"),
- sip_dir,
- os.path.join(cfg.default_mod_dir, "PyQt6", "bindings")):
+for p in (
+ os.path.join(sip_dir, "PyQt6"),
+ os.path.join(sip_dir, "PyQt6-3"),
+ sip_dir,
+ os.path.join(cfg.default_mod_dir, "PyQt6", "bindings"),
+):
if os.path.exists(os.path.join(p, "QtCore", "QtCoremod.sip")):
sip_dir = p
break
diff --git a/cmake/FindQsci.py b/cmake/FindQsci.py
index f74f7c5201e9..de104ba5b105 100644
--- a/cmake/FindQsci.py
+++ b/cmake/FindQsci.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (c) 2012, Larry Shaffer
# All rights reserved.
@@ -30,9 +29,9 @@
.. note:: Redistribution and use is allowed according to the terms of the BSD
license. For details see the accompanying COPYING-CMAKE-SCRIPTS file.
"""
-__author__ = 'Larry Shaffer (larry@dakotacarto.com)'
-__date__ = '22/10/2012'
-__copyright__ = 'Copyright 2012, The QGIS Project'
+__author__ = "Larry Shaffer (larry@dakotacarto.com)"
+__date__ = "22/10/2012"
+__copyright__ = "Copyright 2012, The QGIS Project"
import sys
@@ -40,24 +39,30 @@
if len(sys.argv) > 0:
if sys.argv[1] == "4":
from PyQt4.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
if sys.argv[1] == "5":
from PyQt5.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
else:
from PyQt6.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
else:
try:
from PyQt4.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
except ImportError:
try:
from PyQt5.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
except ImportError:
try:
from PyQt6.Qsci import QSCINTILLA_VERSION_STR
+
VER = QSCINTILLA_VERSION_STR
except ImportError:
pass
diff --git a/cmake/FindSIP.py b/cmake/FindSIP.py
index 2e8eea04bb56..01ff1469528e 100644
--- a/cmake/FindSIP.py
+++ b/cmake/FindSIP.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (c) 2007, Simon Edwards
# All rights reserved.
@@ -38,6 +37,7 @@
print("sip_version_str:%s" % sipbuild.version.SIP_VERSION_STR)
import sysconfig
+
if "deb_system" in sysconfig.get_scheme_names():
python_modules_dir = sysconfig.get_path("purelib", "deb_system")
else:
diff --git a/cmake/PythonCompile.py b/cmake/PythonCompile.py
index 4117aa77065b..a20e9f7c187b 100644
--- a/cmake/PythonCompile.py
+++ b/cmake/PythonCompile.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
-
# By Simon Edwards
# This file is in the public domain.
import py_compile
+
py_compile.main()
diff --git a/editors/QtCreator/templates/wizards/qgis/qgis_test/file.cpp b/editors/QtCreator/templates/wizards/qgis/qgis_test/file.cpp
index cbacf9e7dcfd..2e3b97745511 100644
--- a/editors/QtCreator/templates/wizards/qgis/qgis_test/file.cpp
+++ b/editors/QtCreator/templates/wizards/qgis/qgis_test/file.cpp
@@ -23,39 +23,38 @@
#include
#include
-class %{CN}: public QObject
+class % { CN } : public QObject
{
-
Q_OBJECT
private slots:
- void initTestCase(); // will be called before the first testfunction is executed.
+ void initTestCase(); // will be called before the first testfunction is executed.
void cleanupTestCase(); // will be called after the last testfunction was executed.
- void init(); // will be called before each testfunction is executed.
- void cleanup(); // will be called after every testfunction.
+ void init(); // will be called before each testfunction is executed.
+ void cleanup(); // will be called after every testfunction.
// Add your test methods here
};
-void %{CN}::initTestCase()
+void % { CN }
+::initTestCase()
{
-
}
-void %{CN}::cleanupTestCase()
+void % { CN }
+::cleanupTestCase()
{
-
}
-void %{CN}::init()
+void % { CN }
+::init()
{
-
}
-void %{CN}::cleanup()
+void % { CN }
+::cleanup()
{
-
}
-QGSTEST_MAIN( %{CN} )
+QGSTEST_MAIN( % { CN } )
#include "%{JS: Cpp.classToFileName('%{Class}', '.moc')}"
diff --git a/python/PyQt6/core/additions/edit.py b/python/PyQt6/core/additions/edit.py
index 92756d1e8953..b82f268ea0a9 100644
--- a/python/PyQt6/core/additions/edit.py
+++ b/python/PyQt6/core/additions/edit.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
edit.py
@@ -17,8 +15,6 @@
***************************************************************************
"""
-from builtins import object
-
class QgsEditError(Exception):
@@ -29,7 +25,7 @@ def __str__(self):
return repr(self.value)
-class edit(object):
+class edit:
def __init__(self, layer):
self.layer = layer
diff --git a/python/PyQt6/core/additions/fromfunction.py b/python/PyQt6/core/additions/fromfunction.py
index 92c3647449b5..7bbe1761a975 100644
--- a/python/PyQt6/core/additions/fromfunction.py
+++ b/python/PyQt6/core/additions/fromfunction.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
fromfunction.py
@@ -23,36 +21,38 @@
@staticmethod
-def _fromFunction(description: str,
- function: _typing.Callable,
- *args,
- on_finished: _typing.Optional[_typing.Callable] = None,
- flags=QgsTask.Flag.AllFlags,
- **kwargs) -> QgsTask:
+def _fromFunction(
+ description: str,
+ function: _typing.Callable,
+ *args,
+ on_finished: _typing.Optional[_typing.Callable] = None,
+ flags=QgsTask.Flag.AllFlags,
+ **kwargs
+) -> QgsTask:
"""
-Creates a new QgsTask task from a python function.
+ Creates a new QgsTask task from a python function.
-Example
--------
+ Example
+ -------
-.. code-block:: python
+ .. code-block:: python
- def calculate(task):
- # pretend this is some complex maths and stuff we want
- # to run in the background
- return 5*6
+ def calculate(task):
+ # pretend this is some complex maths and stuff we want
+ # to run in the background
+ return 5*6
- def calculation_finished(exception, value=None):
- if not exception:
- iface.messageBar().pushMessage(
- 'the magic number is {}'.format(value))
- else:
- iface.messageBar().pushMessage(
- str(exception))
+ def calculation_finished(exception, value=None):
+ if not exception:
+ iface.messageBar().pushMessage(
+ 'the magic number is {}'.format(value))
+ else:
+ iface.messageBar().pushMessage(
+ str(exception))
- task = QgsTask.fromFunction('my task', calculate,
- on_finished=calculation_finished)
- QgsApplication.taskManager().addTask(task)
+ task = QgsTask.fromFunction('my task', calculate,
+ on_finished=calculation_finished)
+ QgsApplication.taskManager().addTask(task)
"""
diff --git a/python/PyQt6/core/additions/metaenum.py b/python/PyQt6/core/additions/metaenum.py
index dac7b1b1c464..0d1fb6b39683 100644
--- a/python/PyQt6/core/additions/metaenum.py
+++ b/python/PyQt6/core/additions/metaenum.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
metaenum.py
@@ -61,7 +59,9 @@ def metaEnumFromType(enumClass, baseClass=None, raiseException=True):
return metaEnumFromType(enumClass, baseClass, raiseException)
except AttributeError:
if raiseException:
- raise ValueError("Enum type does not implement baseClass method. Provide the base class as argument.")
+ raise ValueError(
+ "Enum type does not implement baseClass method. Provide the base class as argument."
+ )
try:
meta_object = baseClass.staticMetaObject
@@ -71,7 +71,7 @@ def metaEnumFromType(enumClass, baseClass=None, raiseException=True):
META_ENUM_BY_ENUM_CLASS[enumClass] = meta_enum
except AttributeError:
if raiseException:
- raise TypeError("could not get the metaEnum for {}".format(enumClass.__name__))
+ raise TypeError(f"could not get the metaEnum for {enumClass.__name__}")
meta_enum = None
return meta_enum
diff --git a/python/PyQt6/core/additions/projectdirtyblocker.py b/python/PyQt6/core/additions/projectdirtyblocker.py
index 7f06bd0a8412..95cec0432a79 100644
--- a/python/PyQt6/core/additions/projectdirtyblocker.py
+++ b/python/PyQt6/core/additions/projectdirtyblocker.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
projectdirtyblocker.py
@@ -17,11 +15,10 @@
***************************************************************************
"""
-
from qgis._core import QgsProjectDirtyBlocker
-class ProjectDirtyBlocker():
+class ProjectDirtyBlocker:
"""
Context manager used to block project setDirty calls.
diff --git a/python/PyQt6/core/additions/providermetadata.py b/python/PyQt6/core/additions/providermetadata.py
index d188f6e26fde..b1a676c37386 100644
--- a/python/PyQt6/core/additions/providermetadata.py
+++ b/python/PyQt6/core/additions/providermetadata.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
providermetadata.py
@@ -21,12 +19,12 @@
class PyProviderMetadata(QgsProviderMetadata):
- """ wrapper around QgsProviderMetadata to keep the existing Python code running which registers
- data providers by passing a custom python createProvider() function to QgsProviderMetadata
- constructor. The proper new way of doing it is to subclass QgsProviderMetadata and implement
- its virtual functions.
+ """wrapper around QgsProviderMetadata to keep the existing Python code running which registers
+ data providers by passing a custom python createProvider() function to QgsProviderMetadata
+ constructor. The proper new way of doing it is to subclass QgsProviderMetadata and implement
+ its virtual functions.
- TODO: QGIS 4 - remove this wrapper (only subclassing of QgsProviderMetadata should be used)
+ TODO: QGIS 4 - remove this wrapper (only subclassing of QgsProviderMetadata should be used)
"""
# this is a workaround to keep references to metadata classes
diff --git a/python/PyQt6/core/additions/qgsfeature.py b/python/PyQt6/core/additions/qgsfeature.py
index 252a89fc010e..b3dcef297db1 100644
--- a/python/PyQt6/core/additions/qgsfeature.py
+++ b/python/PyQt6/core/additions/qgsfeature.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgsfeature.py
@@ -22,6 +20,8 @@ def _mapping_feature(feature):
geom = feature.geometry()
fields = [field.name() for field in feature.fields()]
properties = dict(list(zip(fields, feature.attributes())))
- return {'type': 'Feature',
- 'properties': properties,
- 'geometry': geom.__geo_interface__}
+ return {
+ "type": "Feature",
+ "properties": properties,
+ "geometry": geom.__geo_interface__,
+ }
diff --git a/python/PyQt6/core/additions/qgsfunction.py b/python/PyQt6/core/additions/qgsfunction.py
index e91e25a7fc0b..32a338dac291 100644
--- a/python/PyQt6/core/additions/qgsfunction.py
+++ b/python/PyQt6/core/additions/qgsfunction.py
@@ -15,13 +15,18 @@
***************************************************************************
"""
-
import inspect
import string
import traceback
from qgis.PyQt.QtCore import QCoreApplication
-from qgis._core import QgsExpressionFunction, QgsExpression, QgsMessageLog, QgsFeatureRequest, Qgis
+from qgis._core import (
+ QgsExpressionFunction,
+ QgsExpression,
+ QgsMessageLog,
+ QgsFeatureRequest,
+ Qgis,
+)
class QgsPyExpressionFunction(QgsExpressionFunction):
@@ -143,7 +148,8 @@ def register_function(
if not QgsExpression.unregisterFunction(name):
msgtitle = QCoreApplication.translate("UserExpressions", "User expressions")
msg = QCoreApplication.translate(
- "UserExpressions", "The user expression {0} already exists and could not be unregistered."
+ "UserExpressions",
+ "The user expression {0} already exists and could not be unregistered.",
).format(name)
QgsMessageLog.logMessage(msg + "\n", msgtitle, Qgis.MessageLevel.Warning)
return None
@@ -154,7 +160,14 @@ def register_function(
# Legacy: if args was not 'auto', parameters were passed as a list
params_as_list = params_as_list or args != "auto"
f = QgsPyExpressionFunction(
- function, name, group, helptext, usesgeometry, referenced_columns, handlesnull, params_as_list
+ function,
+ name,
+ group,
+ helptext,
+ usesgeometry,
+ referenced_columns,
+ handlesnull,
+ params_as_list,
)
if register:
diff --git a/python/PyQt6/core/additions/qgsgeometry.py b/python/PyQt6/core/additions/qgsgeometry.py
index 6d2babfe1f03..63dbdeb5ccd7 100644
--- a/python/PyQt6/core/additions/qgsgeometry.py
+++ b/python/PyQt6/core/additions/qgsgeometry.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgsgeometry.py
diff --git a/python/PyQt6/core/additions/qgssettings.py b/python/PyQt6/core/additions/qgssettings.py
index a8190a31c7c9..8140211df44c 100644
--- a/python/PyQt6/core/additions/qgssettings.py
+++ b/python/PyQt6/core/additions/qgssettings.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettings.py
@@ -22,7 +20,9 @@
import qgis # required to get base class of enums
-def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_enum_value(
+ self, key, enumDefaultValue, section=QgsSettings.Section.NoSection
+):
"""
Return the setting value for a setting based on an enum.
This forces the output to be a valid and existing entry of the enum.
@@ -41,8 +41,11 @@ def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Sec
meta_enum = metaEnumFromValue(enumDefaultValue)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})"
- .format(enumDefaultValue.__class__))
+ raise ValueError(
+ "could not get the meta enum for given enum default value (type: {})".format(
+ enumDefaultValue.__class__
+ )
+ )
str_val = self.value(key, meta_enum.valueToKey(enumDefaultValue), str, section)
# need a new meta enum as QgsSettings.value is making a copy and leads to seg fault (probably a PyQt issue)
@@ -58,7 +61,9 @@ def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Sec
return enu_val
-def _qgssettings_set_enum_value(self, key, enumValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_set_enum_value(
+ self, key, enumValue, section=QgsSettings.Section.NoSection
+):
"""
Save the setting value for a setting based on an enum.
This forces the output to be a valid and existing entry of the enum.
@@ -76,12 +81,16 @@ def _qgssettings_set_enum_value(self, key, enumValue, section=QgsSettings.Sectio
meta_enum = metaEnumFromValue(enumValue)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})".format(type(enumValue)))
+ raise ValueError(
+ f"could not get the meta enum for given enum default value (type: {type(enumValue)})"
+ )
self.setValue(key, meta_enum.valueToKey(enumValue), section)
-def _qgssettings_flag_value(self, key, flagDefaultValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_flag_value(
+ self, key, flagDefaultValue, section=QgsSettings.Section.NoSection
+):
"""
Return the setting value for a setting based on a flag.
This forces the output to be a valid and existing entry of the enum.
@@ -102,13 +111,19 @@ def _qgssettings_flag_value(self, key, flagDefaultValue, section=QgsSettings.Sec
# dirty hack to get the parent class
__import__(flagDefaultValue.__module__)
baseClass = None
- exec("baseClass={module}.{flag_class}".format(module=flagDefaultValue.__module__.replace('_', ''),
- flag_class=flagDefaultValue.__class__.__name__))
+ exec(
+ "baseClass={module}.{flag_class}".format(
+ module=flagDefaultValue.__module__.replace("_", ""),
+ flag_class=flagDefaultValue.__class__.__name__,
+ )
+ )
meta_enum = metaEnumFromValue(flagDefaultValue, baseClass)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})".format(type(flagDefaultValue)))
+ raise ValueError(
+ f"could not get the meta enum for given enum default value (type: {type(flagDefaultValue)})"
+ )
str_val = self.value(key, meta_enum.valueToKeys(flagDefaultValue), str, section)
# need a new meta enum as QgsSettings.value is making a copy and leads to seg fault (probably a PyQt issue)
diff --git a/python/PyQt6/core/additions/qgssettingsentry.py b/python/PyQt6/core/additions/qgssettingsentry.py
index ca55c90e4f66..d07797c0d55c 100644
--- a/python/PyQt6/core/additions/qgssettingsentry.py
+++ b/python/PyQt6/core/additions/qgssettingsentry.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettingsentry.py
@@ -18,7 +16,13 @@
"""
from .metaenum import metaEnumFromValue
-from qgis.core import QgsSettings, QgsSettingsTree, QgsSettingsEntryBase, QgsLogger, Qgis
+from qgis.core import (
+ QgsSettings,
+ QgsSettingsTree,
+ QgsSettingsEntryBase,
+ QgsLogger,
+ Qgis,
+)
import qgis # required to get base class of enums
@@ -29,7 +33,14 @@ class PyQgsSettingsEntryEnumFlag
since QGIS 3.20
"""
- def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgis.SettingsOptions()):
+ def __init__(
+ self,
+ key,
+ pluginName,
+ defaultValue,
+ description="",
+ options=Qgis.SettingsOptions(),
+ ):
"""
Constructor for PyQgsSettingsEntryEnumFlag.
@@ -42,10 +53,12 @@ def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgi
# TODO QGIS 4: rename pluginName arg to parent and key to name
self.options = options
- defaultValueStr = str()
+ defaultValueStr = ""
self.__metaEnum = metaEnumFromValue(defaultValue)
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
else:
if self.__metaEnum.isFlag():
defaultValueStr = self.__metaEnum.valueToKeys(defaultValue)
@@ -75,9 +88,13 @@ def value(self, dynamicKeyPart=None):
:param dynamicKeyPart: argument specifies the dynamic part of the settings key.
"""
if self.__metaEnum.isFlag():
- return QgsSettings().flagValue(self.key(dynamicKeyPart), self.defaultValue())
+ return QgsSettings().flagValue(
+ self.key(dynamicKeyPart), self.defaultValue()
+ )
else:
- return QgsSettings().enumValue(self.key(dynamicKeyPart), self.defaultValue())
+ return QgsSettings().enumValue(
+ self.key(dynamicKeyPart), self.defaultValue()
+ )
def valueWithDefaultOverride(self, defaultValueOverride, dynamicKeyPart=None):
"""
@@ -87,9 +104,13 @@ def valueWithDefaultOverride(self, defaultValueOverride, dynamicKeyPart=None):
:param dynamicKeyPart: argument specifies the dynamic part of the settings key.
"""
if self.__metaEnum.isFlag():
- return QgsSettings().flagValue(self.key(dynamicKeyPart), defaultValueOverride)
+ return QgsSettings().flagValue(
+ self.key(dynamicKeyPart), defaultValueOverride
+ )
else:
- return QgsSettings().enumValue(self.key(dynamicKeyPart), defaultValueOverride)
+ return QgsSettings().enumValue(
+ self.key(dynamicKeyPart), defaultValueOverride
+ )
def defaultValue(self):
"""
@@ -97,7 +118,9 @@ def defaultValue(self):
"""
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
return -1
defaultValueString = self.defaultValueAsVariant()
@@ -106,7 +129,9 @@ def defaultValue(self):
else:
(defaultValue, ok) = self.__metaEnum.keyToValue(defaultValueString)
if not ok:
- QgsLogger.debug("Invalid enum/flag key/s '{0}'.".format(self.defaultValueAsVariant()))
+ QgsLogger.debug(
+ f"Invalid enum/flag key/s '{self.defaultValueAsVariant()}'."
+ )
return -1
# cast to the enum class
@@ -122,7 +147,9 @@ def setValue(self, value, dynamicKeyPart: (list, str) = None):
"""
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
return False
if self.options & Qgis.SettingsOption.SaveEnumFlagAsInt:
@@ -133,7 +160,7 @@ def setValue(self, value, dynamicKeyPart: (list, str) = None):
else:
enum_flag_key = self.__metaEnum.valueToKey(value)
if not enum_flag_key:
- QgsLogger.debug("Invalid enum/flag value '{0}'.".format(value))
+ QgsLogger.debug(f"Invalid enum/flag value '{value}'.")
return False
if type(dynamicKeyPart) is str:
diff --git a/python/PyQt6/core/additions/qgstaskwrapper.py b/python/PyQt6/core/additions/qgstaskwrapper.py
index 6c2b7560d812..a3a7d8c347bf 100644
--- a/python/PyQt6/core/additions/qgstaskwrapper.py
+++ b/python/PyQt6/core/additions/qgstaskwrapper.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgstaskwrapper.py
@@ -17,7 +15,6 @@
***************************************************************************
"""
-
from qgis._core import QgsTask
@@ -47,7 +44,7 @@ def finished(self, result):
return
if not result and self.exception is None:
- self.exception = Exception('Task canceled')
+ self.exception = Exception("Task canceled")
try:
if self.returned_values:
diff --git a/python/PyQt6/core/additions/readwritecontextentercategory.py b/python/PyQt6/core/additions/readwritecontextentercategory.py
index af9c509342cd..79a9c44e8bac 100644
--- a/python/PyQt6/core/additions/readwritecontextentercategory.py
+++ b/python/PyQt6/core/additions/readwritecontextentercategory.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
readwritecontextentercategory.py
@@ -18,7 +16,7 @@
"""
-class ReadWriteContextEnterCategory():
+class ReadWriteContextEnterCategory:
"""
Push a category to the stack
diff --git a/python/PyQt6/core/additions/runtimeprofiler.py b/python/PyQt6/core/additions/runtimeprofiler.py
index 2216f5609e9d..95758a1ce899 100644
--- a/python/PyQt6/core/additions/runtimeprofiler.py
+++ b/python/PyQt6/core/additions/runtimeprofiler.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
runtimeprofiler.py
@@ -17,11 +15,10 @@
***************************************************************************
"""
-
from qgis._core import QgsScopedRuntimeProfile
-class ScopedRuntimeProfileContextManager():
+class ScopedRuntimeProfileContextManager:
"""
Context manager used to profile blocks of code in the QgsApplication.profiler() registry.
diff --git a/python/PyQt6/core/additions/validitycheck.py b/python/PyQt6/core/additions/validitycheck.py
index 25fdd1586f09..37a32b9cf5e1 100644
--- a/python/PyQt6/core/additions/validitycheck.py
+++ b/python/PyQt6/core/additions/validitycheck.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
validitycheck.py
@@ -16,9 +14,8 @@
* *
***************************************************************************
"""
-from qgis._core import (
- QgsAbstractValidityCheck,
- QgsApplication)
+
+from qgis._core import QgsAbstractValidityCheck, QgsApplication
class CheckFactory:
diff --git a/python/PyQt6/core/contextmanagers.py b/python/PyQt6/core/contextmanagers.py
index 62e0c624659d..72be360097e6 100644
--- a/python/PyQt6/core/contextmanagers.py
+++ b/python/PyQt6/core/contextmanagers.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
contextmanagers.py
@@ -17,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Nathan Woodrow'
-__date__ = 'May 2014'
-__copyright__ = '(C) 2014, Nathan Woodrow'
+__author__ = "Nathan Woodrow"
+__date__ = "May 2014"
+__copyright__ = "(C) 2014, Nathan Woodrow"
import sys
from contextlib import contextmanager
diff --git a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py
index 245b0470a35f..528adb8192a2 100644
--- a/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py
+++ b/python/PyQt6/gui/additions/qgssettingsenumflageditorwrapper.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettingsenumflageditorwrapper.py
@@ -28,7 +26,9 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper):
A settings editor widget wrapper for enum settings as PyQgsSettingsEntryEnumFlag
"""
- def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None):
+ def __init__(
+ self, parent=None, editor=None, setting=None, displayStrings: dict = None
+ ):
self.setting = setting
self.editor = editor
self.displayStrings = {}
@@ -39,19 +39,23 @@ def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict
self.configureEditor(editor, setting)
def id(self):
- return 'py-enum'
+ return "py-enum"
def createWrapper(self, parent=None):
return PyQgsSettingsEnumEditorWidgetWrapper(parent)
def setWidgetFromSetting(self):
if self.setting:
- return self.setWidgetFromVariant(self.setting.valueAsVariant(self.dynamicKeyPartList()))
+ return self.setWidgetFromVariant(
+ self.setting.valueAsVariant(self.dynamicKeyPartList())
+ )
return False
def setSettingFromWidget(self):
if self.editor:
- self.setting.setVariantValue(self.variantValueFromWidget(), self.dynamicKeyPartList())
+ self.setting.setVariantValue(
+ self.variantValueFromWidget(), self.dynamicKeyPartList()
+ )
return True
else:
return False
@@ -88,4 +92,7 @@ def configureEditorPrivate(self, editor: QComboBox, setting: QgsSettingsEntryBas
def enableAutomaticUpdatePrivate(self):
self.editor.currentIndexChanged.connect(
- lambda: self.setting.setValue(self.editor.currentData(), self.dynamicKeyPartList()))
+ lambda: self.setting.setValue(
+ self.editor.currentData(), self.dynamicKeyPartList()
+ )
+ )
diff --git a/python/__init__.py b/python/__init__.py
index bee060f91a58..35fdfd666538 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
__init__.py
@@ -17,11 +15,10 @@
***************************************************************************
"""
-__author__ = 'Martin Dobias'
-__date__ = 'January 2007'
-__copyright__ = '(C) 2007, Martin Dobias'
+__author__ = "Martin Dobias"
+__date__ = "January 2007"
+__copyright__ = "(C) 2007, Martin Dobias"
-from builtins import zip
import os
import sys
@@ -32,7 +29,7 @@ def setupenv():
OSGeo4W package format.
"""
# If the prefix path is already set then we don't do any more path setup.
- if os.getenv('QGIS_PREFIX_PATH'):
+ if os.getenv("QGIS_PREFIX_PATH"):
return
# Setup the paths based on the .vars file.
@@ -41,13 +38,13 @@ def setupenv():
path_split = PurePath(os.path.dirname(os.path.realpath(__file__))).parts
try:
- appname = os.environ['QGIS_ENVNAME']
+ appname = os.environ["QGIS_ENVNAME"]
except KeyError:
appname = path_split[-3]
envfile = list(path_split[:-4])
envfile.append("bin")
- envfile.append("{0}-bin.env".format(appname))
+ envfile.append(f"{appname}-bin.env")
envfile = os.path.join(*envfile)
if not os.path.exists(envfile):
@@ -65,12 +62,14 @@ def setupenv():
pass
-if os.name == 'nt':
+if os.name == "nt":
# On Windows we need to setup the paths before we can import
# any of the QGIS modules or else it will error.
setupenv()
- if sys.version_info[0] > 3 or (sys.version_info[0] == 3 and sys.version_info[1] >= 9):
+ if sys.version_info[0] > 3 or (
+ sys.version_info[0] == 3 and sys.version_info[1] >= 9
+ ):
for p in os.getenv("PATH").split(";"):
if os.path.exists(p):
os.add_dll_directory(p)
@@ -84,11 +83,12 @@ def setupenv():
# (thanks to uic/widget-plugins/qgis_customwidgets.py)
try:
import qgis.gui
+
widget_list = dir(qgis.gui)
# remove widgets that are not allowed as custom widgets (they need to be manually promoted)
- skip_list = ['QgsScrollArea']
+ skip_list = ["QgsScrollArea"]
for widget in widget_list:
- if widget.startswith('Qgs') and widget not in skip_list:
+ if widget.startswith("Qgs") and widget not in skip_list:
sys.modules[widget.lower()] = qgis.gui
except ImportError:
# gui might not be built
diff --git a/python/console/__init__.py b/python/console/__init__.py
index f9477a90b37f..e2f7bf4cd4ad 100644
--- a/python/console/__init__.py
+++ b/python/console/__init__.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
__init__.py
@@ -17,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Salvatore Larosa'
-__date__ = 'September 2012'
-__copyright__ = '(C) 2012, Salvatore Larosa'
+__author__ = "Salvatore Larosa"
+__date__ = "September 2012"
+__copyright__ = "(C) 2012, Salvatore Larosa"
from .console import show_console # NOQA
from .console import init_options_widget
diff --git a/python/console/console.py b/python/console/console.py
index 9da2f97273e7..ba15c4460f1a 100644
--- a/python/console/console.py
+++ b/python/console/console.py
@@ -17,34 +17,50 @@
***************************************************************************/
Some portions of code were taken from https://code.google.com/p/pydee/
"""
+
import os
import subprocess
-from qgis.PyQt.QtCore import Qt, QTimer, QCoreApplication, QSize, QByteArray, QFileInfo, QUrl, QDir
-from qgis.PyQt.QtWidgets import QToolBar, QToolButton, QWidget, QSplitter, QTreeWidget, QAction, QFileDialog, QCheckBox, QSizePolicy, QMenu, QGridLayout, QApplication, QShortcut
-from qgis.PyQt.QtGui import QDesktopServices, QKeySequence
+from qgis.PyQt.QtCore import (
+ Qt,
+ QTimer,
+ QCoreApplication,
+ QSize,
+ QByteArray,
+ QFileInfo,
+ QUrl,
+ QDir,
+)
from qgis.PyQt.QtWidgets import (
- QVBoxLayout,
- QMessageBox
+ QToolBar,
+ QToolButton,
+ QWidget,
+ QSplitter,
+ QTreeWidget,
+ QAction,
+ QFileDialog,
+ QCheckBox,
+ QSizePolicy,
+ QMenu,
+ QGridLayout,
+ QApplication,
+ QShortcut,
)
+from qgis.PyQt.QtGui import QDesktopServices, QKeySequence
+from qgis.PyQt.QtWidgets import QVBoxLayout, QMessageBox
from qgis.utils import iface
from .console_sci import ShellScintilla
from .console_output import ShellOutputScintilla
from .console_editor import EditorTabWidget
from .console_settings import ConsoleOptionsFactory
-from qgis.core import (
- Qgis,
- QgsApplication,
- QgsSettings,
- QgsFileUtils
-)
+from qgis.core import Qgis, QgsApplication, QgsSettings, QgsFileUtils
from qgis.gui import (
QgsFilterLineEdit,
QgsHelp,
QgsDockWidget,
QgsGui,
QgsApplicationExitBlockerInterface,
- QgsCodeEditorDockWidget
+ QgsCodeEditorDockWidget,
)
from functools import partial
@@ -56,13 +72,15 @@
def show_console():
- """ called from QGIS to open the console """
+ """called from QGIS to open the console"""
global _console
if _console is None:
parent = iface.mainWindow() if iface else None
_console = PythonConsole(parent)
if iface:
- _console.visibilityChanged.connect(iface.actionShowPythonDialog().setChecked)
+ _console.visibilityChanged.connect(
+ iface.actionShowPythonDialog().setChecked
+ )
_console.show() # force show even if it was restored as hidden
# set focus to the console so the user can start typing
@@ -89,7 +107,7 @@ def console_displayhook(obj):
def init_options_widget():
- """ called from QGIS to add the console options widget """
+ """called from QGIS to add the console options widget"""
global _options_factory
_options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python"))
iface.registerOptionsWidgetFactory(_options_factory)
@@ -143,13 +161,14 @@ class PythonConsoleWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
- self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console"))
+ self.setWindowTitle(
+ QCoreApplication.translate("PythonConsole", "Python Console")
+ )
self.shell = ShellScintilla(console_widget=self)
self.setFocusProxy(self.shell)
self.shell_output = ShellOutputScintilla(
- console_widget=self,
- shell_editor=self.shell
+ console_widget=self, shell_editor=self.shell
)
self.tabEditorWidget = EditorTabWidget(console_widget=self)
@@ -181,7 +200,7 @@ def __init__(self, parent=None):
self.listClassMethod = QTreeWidget(self.splitterObj)
self.listClassMethod.setColumnCount(2)
objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector")
- self.listClassMethod.setHeaderLabels([objInspLabel, ''])
+ self.listClassMethod.setHeaderLabels([objInspLabel, ""])
self.listClassMethod.setColumnHidden(1, True)
self.listClassMethod.setAlternatingRowColors(True)
@@ -205,17 +224,23 @@ def __init__(self, parent=None):
self.openFileButton = QAction(self)
self.openFileButton.setCheckable(False)
self.openFileButton.setEnabled(True)
- self.openFileButton.setIcon(QgsApplication.getThemeIcon("mActionScriptOpen.svg"))
+ self.openFileButton.setIcon(
+ QgsApplication.getThemeIcon("mActionScriptOpen.svg")
+ )
self.openFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openFileButton.setIconVisibleInMenu(True)
self.openFileButton.setToolTip(openFileBt)
self.openFileButton.setText(openFileBt)
- openExtEditorBt = QCoreApplication.translate("PythonConsole", "Open in External Editor")
+ openExtEditorBt = QCoreApplication.translate(
+ "PythonConsole", "Open in External Editor"
+ )
self.openInEditorButton = QAction(self)
self.openInEditorButton.setCheckable(False)
self.openInEditorButton.setEnabled(True)
- self.openInEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
+ self.openInEditorButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
+ )
self.openInEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openInEditorButton.setIconVisibleInMenu(True)
self.openInEditorButton.setToolTip(openExtEditorBt)
@@ -235,7 +260,9 @@ def __init__(self, parent=None):
self.saveAsFileButton = QAction(self)
self.saveAsFileButton.setCheckable(False)
self.saveAsFileButton.setEnabled(True)
- self.saveAsFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSaveAs.svg"))
+ self.saveAsFileButton.setIcon(
+ QgsApplication.getThemeIcon("mActionFileSaveAs.svg")
+ )
self.saveAsFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.saveAsFileButton.setIconVisibleInMenu(True)
self.saveAsFileButton.setToolTip(saveAsFileBt)
@@ -255,7 +282,9 @@ def __init__(self, parent=None):
self.copyEditorButton = QAction(self)
self.copyEditorButton.setCheckable(False)
self.copyEditorButton.setEnabled(True)
- self.copyEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg"))
+ self.copyEditorButton.setIcon(
+ QgsApplication.getThemeIcon("mActionEditCopy.svg")
+ )
self.copyEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.copyEditorButton.setIconVisibleInMenu(True)
self.copyEditorButton.setToolTip(copyEditorBt)
@@ -265,7 +294,9 @@ def __init__(self, parent=None):
self.pasteEditorButton = QAction(self)
self.pasteEditorButton.setCheckable(False)
self.pasteEditorButton.setEnabled(True)
- self.pasteEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg"))
+ self.pasteEditorButton.setIcon(
+ QgsApplication.getThemeIcon("mActionEditPaste.svg")
+ )
self.pasteEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.pasteEditorButton.setIconVisibleInMenu(True)
self.pasteEditorButton.setToolTip(pasteEditorBt)
@@ -275,7 +306,9 @@ def __init__(self, parent=None):
self.runScriptEditorButton = QAction(self)
self.runScriptEditorButton.setCheckable(False)
self.runScriptEditorButton.setEnabled(True)
- self.runScriptEditorButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
+ self.runScriptEditorButton.setIcon(
+ QgsApplication.getThemeIcon("mActionStart.svg")
+ )
self.runScriptEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runScriptEditorButton.setIconVisibleInMenu(True)
self.runScriptEditorButton.setToolTip(runScriptEditorBt)
@@ -286,7 +319,9 @@ def __init__(self, parent=None):
self.toggleCommentEditorButton = QAction(self)
self.toggleCommentEditorButton.setCheckable(False)
self.toggleCommentEditorButton.setEnabled(True)
- self.toggleCommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"))
+ self.toggleCommentEditorButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg")
+ )
self.toggleCommentEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.toggleCommentEditorButton.setIconVisibleInMenu(True)
self.toggleCommentEditorButton.setToolTip(toggleText + " Ctrl+:")
@@ -297,10 +332,14 @@ def __init__(self, parent=None):
self.reformatCodeEditorButton = QAction(self)
self.reformatCodeEditorButton.setCheckable(False)
self.reformatCodeEditorButton.setEnabled(True)
- self.reformatCodeEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconFormatCode.svg"))
+ self.reformatCodeEditorButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconFormatCode.svg")
+ )
self.reformatCodeEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.reformatCodeEditorButton.setIconVisibleInMenu(True)
- self.reformatCodeEditorButton.setToolTip(reformatCodeText + " Ctrl+Alt+F")
+ self.reformatCodeEditorButton.setToolTip(
+ reformatCodeText + " Ctrl+Alt+F"
+ )
self.reformatCodeEditorButton.setShortcut("Ctrl+Alt+F")
self.reformatCodeEditorButton.setText(reformatCodeText)
@@ -308,9 +347,12 @@ def __init__(self, parent=None):
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
self.objectListButton = QAction(self)
self.objectListButton.setCheckable(True)
- self.objectListButton.setEnabled(QgsSettings().value("pythonConsole/enableObjectInsp",
- False, type=bool))
- self.objectListButton.setIcon(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg"))
+ self.objectListButton.setEnabled(
+ QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool)
+ )
+ self.objectListButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")
+ )
self.objectListButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.objectListButton.setIconVisibleInMenu(True)
self.objectListButton.setToolTip(objList)
@@ -321,7 +363,9 @@ def __init__(self, parent=None):
self.find_text_action = QAction(self)
self.find_text_action.setCheckable(True)
self.find_text_action.setEnabled(True)
- self.find_text_action.setIcon(QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg"))
+ self.find_text_action.setIcon(
+ QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg")
+ )
self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.find_text_action.setIconVisibleInMenu(True)
self.find_text_action.setToolTip(findText)
@@ -330,9 +374,7 @@ def __init__(self, parent=None):
self.tabEditorWidget.search_bar_toggled.connect(
self.find_text_action.setChecked
)
- self.find_text_action.toggled.connect(
- self.tabEditorWidget.toggle_search_bar
- )
+ self.find_text_action.toggled.connect(self.tabEditorWidget.toggle_search_bar)
# ----------------Toolbar Console-------------------------------------
@@ -341,7 +383,9 @@ def __init__(self, parent=None):
self.showEditorButton = QAction(self)
self.showEditorButton.setEnabled(True)
self.showEditorButton.setCheckable(True)
- self.showEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"))
+ self.showEditorButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
+ )
self.showEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.showEditorButton.setIconVisibleInMenu(True)
self.showEditorButton.setToolTip(showEditor)
@@ -351,7 +395,9 @@ def __init__(self, parent=None):
self.clearButton = QAction(self)
self.clearButton.setCheckable(False)
self.clearButton.setEnabled(True)
- self.clearButton.setIcon(QgsApplication.getThemeIcon("console/iconClearConsole.svg"))
+ self.clearButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconClearConsole.svg")
+ )
self.clearButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.clearButton.setIconVisibleInMenu(True)
self.clearButton.setToolTip(clearBt)
@@ -361,7 +407,9 @@ def __init__(self, parent=None):
self.optionsButton = QAction(self)
self.optionsButton.setCheckable(False)
self.optionsButton.setEnabled(True)
- self.optionsButton.setIcon(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"))
+ self.optionsButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")
+ )
self.optionsButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.optionsButton.setIconVisibleInMenu(True)
self.optionsButton.setToolTip(optionsBt)
@@ -380,13 +428,19 @@ def __init__(self, parent=None):
# Help button
self.helpConsoleAction = QAction(self)
self.helpConsoleAction.setEnabled(True)
- self.helpConsoleAction.setText(QCoreApplication.translate("PythonConsole", "Python Console Help"))
+ self.helpConsoleAction.setText(
+ QCoreApplication.translate("PythonConsole", "Python Console Help")
+ )
self.helpAPIAction = QAction(self)
self.helpAPIAction.setEnabled(True)
- self.helpAPIAction.setText(QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation"))
+ self.helpAPIAction.setText(
+ QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")
+ )
self.helpCookbookAction = QAction(self)
self.helpCookbookAction.setEnabled(True)
- self.helpCookbookAction.setText(QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook"))
+ self.helpCookbookAction.setText(
+ QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")
+ )
self.helpMenu = QMenu(self)
self.helpMenu.addAction(self.helpConsoleAction)
@@ -397,7 +451,9 @@ def __init__(self, parent=None):
self.helpButton = QToolButton(self)
self.helpButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
self.helpButton.setEnabled(True)
- self.helpButton.setIcon(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"))
+ self.helpButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconHelpConsole.svg")
+ )
self.helpButton.setToolTip(helpBt)
self.helpButton.setMenu(self.helpMenu)
@@ -457,16 +513,22 @@ def __init__(self, parent=None):
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.widgetButtonEditor.sizePolicy().hasHeightForWidth())
+ sizePolicy.setHeightForWidth(
+ self.widgetButtonEditor.sizePolicy().hasHeightForWidth()
+ )
self.widgetButtonEditor.setSizePolicy(sizePolicy)
- sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ sizePolicy = QSizePolicy(
+ QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.shell_output.sizePolicy().hasHeightForWidth())
self.shell_output.setSizePolicy(sizePolicy)
- self.shell_output.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
+ self.shell_output.setVerticalScrollBarPolicy(
+ Qt.ScrollBarPolicy.ScrollBarAsNeeded
+ )
self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
# ------------ Layout -------------------------------
@@ -523,9 +585,17 @@ def allowExit(self):
tab_index = tab_count - i - 1
tab_widget = self.tabEditorWidget.widget(tab_index)
if tab_widget.isModified():
- ret = QMessageBox.question(self, self.tr("Save {}").format(self.tabEditorWidget.tabText(tab_index)),
- self.tr("There are unsaved changes in this script. Do you want to keep those?"),
- QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Discard, QMessageBox.StandardButton.Cancel)
+ ret = QMessageBox.question(
+ self,
+ self.tr("Save {}").format(self.tabEditorWidget.tabText(tab_index)),
+ self.tr(
+ "There are unsaved changes in this script. Do you want to keep those?"
+ ),
+ QMessageBox.StandardButton.Save
+ | QMessageBox.StandardButton.Cancel
+ | QMessageBox.StandardButton.Discard,
+ QMessageBox.StandardButton.Cancel,
+ )
if ret == QMessageBox.StandardButton.Save:
tab_widget.save()
if tab_widget.isModified():
@@ -545,14 +615,14 @@ def _toggleFind(self):
def onClickGoToLine(self, item, column):
tabEditor = self.tabEditorWidget.currentWidget()
- if item.text(1) == 'syntaxError':
+ if item.text(1) == "syntaxError":
check = tabEditor.syntaxCheck()
if check and not tabEditor.isReadOnly():
self.tabEditorWidget.currentWidget().save()
return
linenr = int(item.text(1))
itemName = str(item.text(0))
- charPos = itemName.find(' ')
+ charPos = itemName.find(" ")
if charPos != -1:
objName = itemName[0:charPos]
else:
@@ -595,7 +665,8 @@ def openScriptFile(self):
lastDirPath = settings.value("pythonConsole/lastDirPath", QDir.homePath())
openFileTr = QCoreApplication.translate("PythonConsole", "Open File")
fileList, selected_filter = QFileDialog.getOpenFileNames(
- self, openFileTr, lastDirPath, "Script file (*.py)")
+ self, openFileTr, lastDirPath, "Script file (*.py)"
+ )
if fileList:
for pyFile in fileList:
for i in range(self.tabEditorWidget.count()):
@@ -609,16 +680,16 @@ def openScriptFile(self):
lastDirPath = QFileInfo(pyFile).path()
settings.setValue("pythonConsole/lastDirPath", pyFile)
- self.updateTabListScript(pyFile, action='append')
+ self.updateTabListScript(pyFile, action="append")
def saveScriptFile(self):
tabWidget = self.tabEditorWidget.currentWidget()
try:
tabWidget.save()
- except (IOError, OSError) as error:
- msgText = QCoreApplication.translate('PythonConsole',
- 'The file {0} could not be saved. Error: {1}').format(tabWidget.file_path(),
- error.strerror)
+ except OSError as error:
+ msgText = QCoreApplication.translate(
+ "PythonConsole", "The file {0} could not be saved. Error: {1}"
+ ).format(tabWidget.file_path(), error.strerror)
self.callWidgetMessageBarEditor(msgText, Qgis.MessageLevel.Critical)
def saveAsScriptFile(self, index=None):
@@ -626,9 +697,8 @@ def saveAsScriptFile(self, index=None):
if not index:
index = self.tabEditorWidget.currentIndex()
if not tabWidget.file_path():
- fileName = self.tabEditorWidget.tabText(index).replace('*', '')
- fileName = QgsFileUtils.ensureFileNameHasExtension(fileName,
- ['py'])
+ fileName = self.tabEditorWidget.tabText(index).replace("*", "")
+ fileName = QgsFileUtils.ensureFileNameHasExtension(fileName, ["py"])
folder = QgsSettings().value("pythonConsole/lastDirPath", QDir.homePath())
pathFileName = os.path.join(folder, fileName)
fileNone = True
@@ -636,18 +706,19 @@ def saveAsScriptFile(self, index=None):
pathFileName = tabWidget.file_path()
fileNone = False
saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As")
- filename, filter = QFileDialog.getSaveFileName(self,
- saveAsFileTr,
- pathFileName, "Script file (*.py)")
+ filename, filter = QFileDialog.getSaveFileName(
+ self, saveAsFileTr, pathFileName, "Script file (*.py)"
+ )
if filename:
- filename = QgsFileUtils.ensureFileNameHasExtension(filename, ['py'])
+ filename = QgsFileUtils.ensureFileNameHasExtension(filename, ["py"])
try:
tabWidget.save(filename)
- except (IOError, OSError) as error:
- msgText = QCoreApplication.translate('PythonConsole',
- 'The file {0} could not be saved. Error: {1}').format(tabWidget.file_path(),
- error.strerror)
+ except OSError as error:
+ msgText = QCoreApplication.translate(
+ "PythonConsole",
+ "The file {0} could not be saved. Error: {1}",
+ ).format(tabWidget.file_path(), error.strerror)
self.callWidgetMessageBarEditor(msgText, Qgis.MessageLevel.Critical)
if fileNone:
tabWidget.set_file_path(None)
@@ -656,23 +727,29 @@ def saveAsScriptFile(self, index=None):
return
if not fileNone:
- self.updateTabListScript(pathFileName, action='remove')
+ self.updateTabListScript(pathFileName, action="remove")
def openHelpConsole(self):
QgsHelp.openHelp("plugins/python_console.html")
def openHelpAPI(self):
- m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION)
+ m = re.search(r"^([0-9]+)\.([0-9]+)\.", Qgis.QGIS_VERSION)
if m:
- QDesktopServices.openUrl(QUrl('https://qgis.org/pyqgis/{}.{}/'.format(m.group(1), m.group(2))))
+ QDesktopServices.openUrl(
+ QUrl(f"https://qgis.org/pyqgis/{m.group(1)}.{m.group(2)}/")
+ )
def openHelpCookbook(self):
- m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION)
+ m = re.search(r"^([0-9]+)\.([0-9]+)\.", Qgis.QGIS_VERSION)
if m:
- QDesktopServices.openUrl(QUrl('https://docs.qgis.org/{}.{}/en/docs/pyqgis_developer_cookbook/index.html'.format(m.group(1), m.group(2))))
+ QDesktopServices.openUrl(
+ QUrl(
+ f"https://docs.qgis.org/{m.group(1)}.{m.group(2)}/en/docs/pyqgis_developer_cookbook/index.html"
+ )
+ )
def openSettings(self):
- iface.showOptionsDialog(iface.mainWindow(), currentPage='consoleOptions')
+ iface.showOptionsDialog(iface.mainWindow(), currentPage="consoleOptions")
def updateSettings(self):
self.shell.refreshSettingsShell()
@@ -686,23 +763,24 @@ def callWidgetMessageBarEditor(self, text, level):
self.tabEditorWidget.showMessage(text, level)
def updateTabListScript(self, script, action=None):
- if action == 'remove':
+ if action == "remove":
self.tabListScript.remove(script)
- elif action == 'append':
+ elif action == "append":
if not self.tabListScript:
self.tabListScript = []
if script not in self.tabListScript:
self.tabListScript.append(script)
else:
self.tabListScript = []
- QgsSettings().setValue("pythonConsole/tabScripts",
- self.tabListScript)
+ QgsSettings().setValue("pythonConsole/tabScripts", self.tabListScript)
def saveSettingsConsole(self):
settings = QgsSettings()
settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState())
settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState())
- settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState())
+ settings.setValue(
+ "pythonConsole/splitterEditor", self.splitterEditor.saveState()
+ )
self.shell.writeHistoryFile()
@@ -710,12 +788,18 @@ def restoreSettingsConsole(self):
settings = QgsSettings()
storedTabScripts = settings.value("pythonConsole/tabScripts", [])
self.tabListScript = storedTabScripts
- self.splitter.restoreState(settings.value("pythonConsole/splitterConsole", QByteArray()))
- self.splitterEditor.restoreState(settings.value("pythonConsole/splitterEditor", QByteArray()))
- self.splitterObj.restoreState(settings.value("pythonConsole/splitterObj", QByteArray()))
+ self.splitter.restoreState(
+ settings.value("pythonConsole/splitterConsole", QByteArray())
+ )
+ self.splitterEditor.restoreState(
+ settings.value("pythonConsole/splitterEditor", QByteArray())
+ )
+ self.splitterObj.restoreState(
+ settings.value("pythonConsole/splitterObj", QByteArray())
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
a = QApplication(sys.argv)
console = PythonConsoleWidget()
console.show()
diff --git a/python/console/console_compile_apis.py b/python/console/console_compile_apis.py
index 9ea869646b34..3b112270b54c 100644
--- a/python/console/console_compile_apis.py
+++ b/python/console/console_compile_apis.py
@@ -27,7 +27,9 @@
from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox
from qgis.PyQt.QtCore import QCoreApplication
-Ui_APIsDialogPythonConsole, _ = uic.loadUiType(Path(__file__).parent / 'console_compile_apis.ui')
+Ui_APIsDialogPythonConsole, _ = uic.loadUiType(
+ Path(__file__).parent / "console_compile_apis.ui"
+)
class PrepareAPIDialog(QDialog):
@@ -61,21 +63,24 @@ def _preparationFinished(self):
self._clearLexer()
if os.path.exists(self._pap_file):
os.remove(self._pap_file)
- self.ui.label.setText(QCoreApplication.translate("PythonConsole", "Saving prepared file…"))
+ self.ui.label.setText(
+ QCoreApplication.translate("PythonConsole", "Saving prepared file…")
+ )
prepd = self._api.savePrepared(self._pap_file)
rslt = self.tr("Error")
if prepd:
rslt = QCoreApplication.translate("PythonConsole", "Saved")
- self.ui.label.setText('{0} {1}'.format(self.ui.label.text(), rslt))
+ self.ui.label.setText(f"{self.ui.label.text()} {rslt}")
self._api = None
self.ui.progressBar.setVisible(False)
self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(
- QCoreApplication.translate("PythonConsole", "Done"))
+ QCoreApplication.translate("PythonConsole", "Done")
+ )
self.adjustSize()
def prepareAPI(self):
# self.ui.textEdit_Qsci.setLexer(0)
- exec('self.qlexer = {0}(self.ui.textEdit_Qsci)'.format(self._api_lexer))
+ exec(f"self.qlexer = {self._api_lexer}(self.ui.textEdit_Qsci)")
# self.ui.textEdit_Qsci.setLexer(self.qlexer)
self._api = QsciAPIs(self.qlexer)
self._api.apiPreparationFinished.connect(self._preparationFinished)
@@ -86,9 +91,13 @@ def prepareAPI(self):
except Exception as err:
self._api = None
self._clearLexer()
- self.ui.label.setText(QCoreApplication.translate("PythonConsole", "Error preparing file…"))
+ self.ui.label.setText(
+ QCoreApplication.translate("PythonConsole", "Error preparing file…")
+ )
self.ui.progressBar.setVisible(False)
self.ui.plainTextEdit.setVisible(True)
self.ui.plainTextEdit.insertPlainText(err)
- self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(self.tr("Done"))
+ self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(
+ self.tr("Done")
+ )
self.adjustSize()
diff --git a/python/console/console_editor.py b/python/console/console_editor.py
index edf83b352ec8..9e1eb20dabfe 100644
--- a/python/console/console_editor.py
+++ b/python/console/console_editor.py
@@ -17,6 +17,7 @@
***************************************************************************/
Some portions of code were taken from https://code.google.com/p/pydee/
"""
+
from __future__ import annotations
import codecs
@@ -26,20 +27,13 @@
import re
import sys
import tempfile
-from typing import (
- Optional,
- TYPE_CHECKING
-)
+from typing import Optional, TYPE_CHECKING
from functools import partial
from operator import itemgetter
from pathlib import Path
from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings
-from qgis.gui import (
- QgsCodeEditorPython,
- QgsCodeEditorWidget,
- QgsMessageBar
-)
+from qgis.gui import QgsCodeEditorPython, QgsCodeEditorWidget, QgsMessageBar
from qgis.PyQt.Qsci import QsciScintilla
from qgis.PyQt.QtCore import (
@@ -52,7 +46,7 @@
QJsonDocument,
QSize,
Qt,
- QUrl
+ QUrl,
)
from qgis.PyQt.QtGui import QKeySequence
from qgis.PyQt.QtNetwork import QNetworkRequest
@@ -83,41 +77,57 @@ class Editor(QgsCodeEditorPython):
trigger_find = pyqtSignal()
- def __init__(self,
- editor_tab: EditorTab,
- console_widget: PythonConsoleWidget,
- tab_widget: EditorTabWidget):
+ def __init__(
+ self,
+ editor_tab: EditorTab,
+ console_widget: PythonConsoleWidget,
+ tab_widget: EditorTabWidget,
+ ):
super().__init__(editor_tab)
self.editor_tab: EditorTab = editor_tab
self.console_widget: PythonConsoleWidget = console_widget
self.tab_widget: EditorTabWidget = tab_widget
- self.code_editor_widget: Optional[QgsCodeEditorWidget] = None
+ self.code_editor_widget: QgsCodeEditorWidget | None = None
self.setMinimumHeight(120)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
# Disable default scintilla shortcuts
ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) # Switch current line with the next one
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) # Duplicate current line / selection
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) # Delete current line
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift)
+ self.SendScintilla(
+ QsciScintilla.SCI_CLEARCMDKEY, ord("T") + ctrl
+ ) # Switch current line with the next one
+ self.SendScintilla(
+ QsciScintilla.SCI_CLEARCMDKEY, ord("D") + ctrl
+ ) # Duplicate current line / selection
+ self.SendScintilla(
+ QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl
+ ) # Delete current line
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl + shift)
# New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete
- self.newShortcutCS = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Space), self)
+ self.newShortcutCS = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Space), self
+ )
self.newShortcutCS.setContext(Qt.ShortcutContext.WidgetShortcut)
- self.redoScut = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_Z), self)
+ self.redoScut = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_Z), self
+ )
self.redoScut.setContext(Qt.ShortcutContext.WidgetShortcut)
self.redoScut.activated.connect(self.redo)
self.newShortcutCS.activated.connect(self.autoComplete)
self.runScut = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_E), self)
self.runScut.setContext(Qt.ShortcutContext.WidgetShortcut)
self.runScut.activated.connect(self.runSelectedCode) # spellok
- self.runScriptScut = QShortcut(QKeySequence(Qt.Modifier.SHIFT | Qt.Modifier.CTRL | Qt.Key.Key_E), self)
+ self.runScriptScut = QShortcut(
+ QKeySequence(Qt.Modifier.SHIFT | Qt.Modifier.CTRL | Qt.Key.Key_E), self
+ )
self.runScriptScut.setContext(Qt.ShortcutContext.WidgetShortcut)
self.runScriptScut.activated.connect(self.runScriptCode)
- self.syntaxCheckScut = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_4), self)
+ self.syntaxCheckScut = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_4), self
+ )
self.syntaxCheckScut.setContext(Qt.ShortcutContext.WidgetShortcut)
self.syntaxCheckScut.activated.connect(self.syntaxCheck)
self.modificationChanged.connect(self.editor_tab.modified)
@@ -136,85 +146,105 @@ def settingsEditor(self):
def contextMenuEvent(self, e):
menu = QMenu(self)
menu.addAction(
- QCoreApplication.translate("PythonConsole", "Hide Editor"),
- self.hideEditor)
+ QCoreApplication.translate("PythonConsole", "Hide Editor"), self.hideEditor
+ )
menu.addSeparator()
- syntaxCheckAction = QAction(QgsApplication.getThemeIcon("console/iconSyntaxErrorConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Check Syntax"),
- menu)
+ syntaxCheckAction = QAction(
+ QgsApplication.getThemeIcon("console/iconSyntaxErrorConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Check Syntax"),
+ menu,
+ )
syntaxCheckAction.triggered.connect(self.syntaxCheck)
- syntaxCheckAction.setShortcut('Ctrl+4')
+ syntaxCheckAction.setShortcut("Ctrl+4")
menu.addAction(syntaxCheckAction)
- runSelected = QAction(QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), # spellok
- QCoreApplication.translate("PythonConsole", "Run Selected"),
- menu)
+ runSelected = QAction(
+ QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), # spellok
+ QCoreApplication.translate("PythonConsole", "Run Selected"),
+ menu,
+ )
runSelected.triggered.connect(self.runSelectedCode) # spellok
- runSelected.setShortcut('Ctrl+E') # spellok
+ runSelected.setShortcut("Ctrl+E") # spellok
menu.addAction(runSelected) # spellok
- pyQGISHelpAction = QAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Search Selection in PyQGIS Documentation"),
- menu)
+ pyQGISHelpAction = QAction(
+ QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
+ QCoreApplication.translate(
+ "PythonConsole", "Search Selection in PyQGIS Documentation"
+ ),
+ menu,
+ )
pyQGISHelpAction.triggered.connect(self.searchSelectedTextInPyQGISDocs)
menu.addAction(pyQGISHelpAction)
- start_action = QAction(QgsApplication.getThemeIcon("mActionStart.svg"),
- QCoreApplication.translate("PythonConsole", "Run Script"),
- menu)
+ start_action = QAction(
+ QgsApplication.getThemeIcon("mActionStart.svg"),
+ QCoreApplication.translate("PythonConsole", "Run Script"),
+ menu,
+ )
start_action.triggered.connect(self.runScriptCode)
- start_action.setShortcut('Ctrl+Shift+E')
+ start_action.setShortcut("Ctrl+Shift+E")
menu.addAction(start_action)
menu.addSeparator()
- undoAction = QAction(QgsApplication.getThemeIcon("mActionUndo.svg"),
- QCoreApplication.translate("PythonConsole", "Undo"),
- menu)
+ undoAction = QAction(
+ QgsApplication.getThemeIcon("mActionUndo.svg"),
+ QCoreApplication.translate("PythonConsole", "Undo"),
+ menu,
+ )
undoAction.triggered.connect(self.undo)
undoAction.setShortcut(QKeySequence.StandardKey.Undo)
menu.addAction(undoAction)
- redoAction = QAction(QgsApplication.getThemeIcon("mActionRedo.svg"),
- QCoreApplication.translate("PythonConsole", "Redo"),
- menu)
+ redoAction = QAction(
+ QgsApplication.getThemeIcon("mActionRedo.svg"),
+ QCoreApplication.translate("PythonConsole", "Redo"),
+ menu,
+ )
redoAction.triggered.connect(self.redo)
- redoAction.setShortcut('Ctrl+Shift+Z')
+ redoAction.setShortcut("Ctrl+Shift+Z")
menu.addAction(redoAction)
menu.addSeparator()
find_action = QAction(
QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg"),
QCoreApplication.translate("PythonConsole", "Find Text"),
- menu)
+ menu,
+ )
find_action.triggered.connect(self.trigger_find)
menu.addAction(find_action)
cutAction = QAction(
QgsApplication.getThemeIcon("mActionEditCut.svg"),
QCoreApplication.translate("PythonConsole", "Cut"),
- menu)
+ menu,
+ )
cutAction.triggered.connect(self.cut)
cutAction.setShortcut(QKeySequence.StandardKey.Cut)
menu.addAction(cutAction)
- copyAction = QAction(QgsApplication.getThemeIcon("mActionEditCopy.svg"),
- QCoreApplication.translate("PythonConsole", "Copy"),
- menu)
+ copyAction = QAction(
+ QgsApplication.getThemeIcon("mActionEditCopy.svg"),
+ QCoreApplication.translate("PythonConsole", "Copy"),
+ menu,
+ )
copyAction.triggered.connect(self.copy)
copyAction.setShortcut(QKeySequence.StandardKey.Copy)
menu.addAction(copyAction)
- pasteAction = QAction(QgsApplication.getThemeIcon("mActionEditPaste.svg"),
- QCoreApplication.translate("PythonConsole", "Paste"),
- menu)
+ pasteAction = QAction(
+ QgsApplication.getThemeIcon("mActionEditPaste.svg"),
+ QCoreApplication.translate("PythonConsole", "Paste"),
+ menu,
+ )
pasteAction.triggered.connect(self.paste)
pasteAction.setShortcut(QKeySequence.StandardKey.Paste)
menu.addAction(pasteAction)
selectAllAction = QAction(
- QCoreApplication.translate("PythonConsole", "Select All"),
- menu)
+ QCoreApplication.translate("PythonConsole", "Select All"), menu
+ )
selectAllAction.triggered.connect(self.selectAll)
selectAllAction.setShortcut(QKeySequence.StandardKey.SelectAll)
menu.addAction(selectAllAction)
@@ -223,27 +253,38 @@ def contextMenuEvent(self, e):
toggle_comment_action = QAction(
QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"),
QCoreApplication.translate("PythonConsole", "Toggle Comment"),
- menu)
+ menu,
+ )
toggle_comment_action.triggered.connect(self.toggleComment)
- toggle_comment_action.setShortcut('Ctrl+:')
+ toggle_comment_action.setShortcut("Ctrl+:")
menu.addAction(toggle_comment_action)
menu.addSeparator()
gist_menu = QMenu(self)
- gist_menu.setTitle(QCoreApplication.translate("PythonConsole", "Share on GitHub"))
+ gist_menu.setTitle(
+ QCoreApplication.translate("PythonConsole", "Share on GitHub")
+ )
gist_menu.setIcon(QgsApplication.getThemeIcon("console/iconCodepadConsole.svg"))
- gist_menu.addAction(QCoreApplication.translate("PythonConsole", "Secret Gist"),
- partial(self.shareOnGist, False))
- gist_menu.addAction(QCoreApplication.translate("PythonConsole", "Public Gist"),
- partial(self.shareOnGist, True))
+ gist_menu.addAction(
+ QCoreApplication.translate("PythonConsole", "Secret Gist"),
+ partial(self.shareOnGist, False),
+ )
+ gist_menu.addAction(
+ QCoreApplication.translate("PythonConsole", "Public Gist"),
+ partial(self.shareOnGist, True),
+ )
menu.addMenu(gist_menu)
- showCodeInspection = menu.addAction(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Hide/Show Object Inspector"),
- self.objectListEditor)
+ showCodeInspection = menu.addAction(
+ QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Hide/Show Object Inspector"),
+ self.objectListEditor,
+ )
menu.addSeparator()
- menu.addAction(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Options…"),
- self.console_widget.openSettings)
+ menu.addAction(
+ QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Options…"),
+ self.console_widget.openSettings,
+ )
syntaxCheckAction.setEnabled(False)
pasteAction.setEnabled(False)
pyQGISHelpAction.setEnabled(False)
@@ -259,7 +300,7 @@ def contextMenuEvent(self, e):
copyAction.setEnabled(True)
cutAction.setEnabled(True)
pyQGISHelpAction.setEnabled(True)
- if not self.text() == '':
+ if not self.text() == "":
selectAllAction.setEnabled(True)
syntaxCheckAction.setEnabled(True)
if self.isUndoAvailable():
@@ -268,8 +309,7 @@ def contextMenuEvent(self, e):
redoAction.setEnabled(True)
if QApplication.clipboard().text():
pasteAction.setEnabled(True)
- if QgsSettings().value("pythonConsole/enableObjectInsp",
- False, type=bool):
+ if QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool):
showCodeInspection.setEnabled(True)
menu.exec(self.mapToGlobal(e.pos()))
@@ -292,7 +332,7 @@ def hideEditor(self):
def createTempFile(self):
name = tempfile.NamedTemporaryFile(delete=False).name
# Need to use newline='' to avoid adding extra \r characters on Windows
- with open(name, 'w', encoding='utf-8', newline='') as f:
+ with open(name, "w", encoding="utf-8", newline="") as f:
f.write(self.text())
return name
@@ -300,8 +340,9 @@ def runScriptCode(self):
autoSave = QgsSettings().value("pythonConsole/autoSaveScript", False, type=bool)
filename = self.code_editor_widget.filePath()
filename_override = None
- msgEditorBlank = QCoreApplication.translate('PythonConsole',
- 'Hey, type something to run!')
+ msgEditorBlank = QCoreApplication.translate(
+ "PythonConsole", "Hey, type something to run!"
+ )
if filename is None:
if not self.isModified():
self.showMessage(msgEditorBlank)
@@ -314,8 +355,10 @@ def runScriptCode(self):
elif not filename or self.isModified():
# Create a new temp file if the file isn't already saved.
filename = self.createTempFile()
- filename_override = self.tab_widget.tabText(self.tab_widget.currentIndex())
- if filename_override.startswith('*'):
+ filename_override = self.tab_widget.tabText(
+ self.tab_widget.currentIndex()
+ )
+ if filename_override.startswith("*"):
filename_override = filename_override[1:]
deleteTempFile = True
@@ -337,10 +380,14 @@ def getTextFromEditor(self):
def goToLine(self, objName, linenr):
self.SendScintilla(QsciScintilla.SCI_GOTOLINE, linenr - 1)
- self.SendScintilla(QsciScintilla.SCI_SETTARGETSTART,
- self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS))
+ self.SendScintilla(
+ QsciScintilla.SCI_SETTARGETSTART,
+ self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS),
+ )
self.SendScintilla(QsciScintilla.SCI_SETTARGETEND, len(self.text()))
- pos = self.SendScintilla(QsciScintilla.SCI_SEARCHINTARGET, len(objName), objName)
+ pos = self.SendScintilla(
+ QsciScintilla.SCI_SEARCHINTARGET, len(objName), objName
+ )
index = pos - self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS)
# line, _ = self.getCursorPosition()
self.setSelection(linenr - 1, index, linenr - 1, index + len(objName))
@@ -368,11 +415,13 @@ def loaded_external_changes(self):
self.tab_widget.listObject(self.tab_widget.currentWidget())
def fileReadOnly(self):
- msgText = QCoreApplication.translate('PythonConsole',
- 'The file "{0}" is read only, please save to different file first.').format(self.code_editor_widget.filePath())
+ msgText = QCoreApplication.translate(
+ "PythonConsole",
+ 'The file "{0}" is read only, please save to different file first.',
+ ).format(self.code_editor_widget.filePath())
self.showMessage(msgText)
- def save(self, filename: Optional[str] = None):
+ def save(self, filename: str | None = None):
if self.isReadOnly():
return
@@ -381,13 +430,18 @@ def save(self, filename: Optional[str] = None):
index = self.tab_widget.indexOf(self.editor_tab)
if not filename and not self.code_editor_widget.filePath():
- saveTr = QCoreApplication.translate('PythonConsole',
- 'Python Console: Save file')
+ saveTr = QCoreApplication.translate(
+ "PythonConsole", "Python Console: Save file"
+ )
folder = QgsSettings().value("pythonConsole/lastDirPath", QDir.homePath())
- path, filter = QFileDialog().getSaveFileName(self,
- saveTr,
- os.path.join(folder, self.tab_widget.tabText(index).replace('*', '') + '.py'),
- "Script file (*.py)")
+ path, filter = QFileDialog().getSaveFileName(
+ self,
+ saveTr,
+ os.path.join(
+ folder, self.tab_widget.tabText(index).replace("*", "") + ".py"
+ ),
+ "Script file (*.py)",
+ )
# If the user didn't select a file, abort the save operation
if not path:
self.code_editor_widget.setFilePath(None)
@@ -396,29 +450,40 @@ def save(self, filename: Optional[str] = None):
self.code_editor_widget.save(filename)
- msgText = QCoreApplication.translate('PythonConsole',
- 'Script was correctly saved.')
+ msgText = QCoreApplication.translate(
+ "PythonConsole", "Script was correctly saved."
+ )
self.showMessage(msgText)
# Save the new contents
# Need to use newline='' to avoid adding extra \r characters on Windows
- with open(self.code_editor_widget.filePath(), 'w', encoding='utf-8', newline='') as f:
+ with open(
+ self.code_editor_widget.filePath(), "w", encoding="utf-8", newline=""
+ ) as f:
f.write(self.text())
- self.tab_widget.setTabTitle(index, Path(self.code_editor_widget.filePath()).name)
+ self.tab_widget.setTabTitle(
+ index, Path(self.code_editor_widget.filePath()).name
+ )
self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath())
self.setModified(False)
self.console_widget.saveFileButton.setEnabled(False)
- self.console_widget.updateTabListScript(self.code_editor_widget.filePath(), action='append')
+ self.console_widget.updateTabListScript(
+ self.code_editor_widget.filePath(), action="append"
+ )
self.tab_widget.listObject(self.editor_tab)
- QgsSettings().setValue("pythonConsole/lastDirPath",
- Path(self.code_editor_widget.filePath()).parent.as_posix())
+ QgsSettings().setValue(
+ "pythonConsole/lastDirPath",
+ Path(self.code_editor_widget.filePath()).parent.as_posix(),
+ )
def event(self, e):
- """ Used to override the Application shortcuts when the editor has focus """
+ """Used to override the Application shortcuts when the editor has focus"""
if e.type() == QEvent.Type.ShortcutOverride:
ctrl = e.modifiers() == Qt.KeyboardModifier.ControlModifier
- ctrl_shift = e.modifiers() == (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
+ ctrl_shift = e.modifiers() == (
+ Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier
+ )
if (
(ctrl and e.key() == Qt.Key.Key_W)
or (ctrl_shift and e.key() == Qt.Key.Key_W)
@@ -434,7 +499,9 @@ def event(self, e):
def keyPressEvent(self, e):
ctrl = e.modifiers() == Qt.KeyboardModifier.ControlModifier
- ctrl_shift = e.modifiers() == (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
+ ctrl_shift = e.modifiers() == (
+ Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier
+ )
# Ctrl+W: close current tab
if ctrl and e.key() == Qt.Key.Key_W:
@@ -458,10 +525,9 @@ def keyPressEvent(self, e):
super().keyPressEvent(e)
- def showMessage(self,
- text: str,
- title: Optional[str] = None,
- level=Qgis.MessageLevel.Info):
+ def showMessage(
+ self, text: str, title: str | None = None, level=Qgis.MessageLevel.Info
+ ):
self.editor_tab.showMessage(text, level, title=title)
@@ -469,29 +535,25 @@ class EditorTab(QWidget):
search_bar_toggled = pyqtSignal(bool)
- def __init__(self,
- tab_widget: EditorTabWidget,
- console_widget: PythonConsoleWidget,
- filename: Optional[str],
- read_only: bool):
+ def __init__(
+ self,
+ tab_widget: EditorTabWidget,
+ console_widget: PythonConsoleWidget,
+ filename: str | None,
+ read_only: bool,
+ ):
super().__init__(tab_widget)
self.tab_widget: EditorTabWidget = tab_widget
- self._editor = Editor(editor_tab=self,
- console_widget=console_widget,
- tab_widget=tab_widget)
-
- self._editor_code_widget = QgsCodeEditorWidget(
- self._editor
+ self._editor = Editor(
+ editor_tab=self, console_widget=console_widget, tab_widget=tab_widget
)
+
+ self._editor_code_widget = QgsCodeEditorWidget(self._editor)
self._editor.set_code_editor_widget(self._editor_code_widget)
- self._editor_code_widget.searchBarToggled.connect(
- self.search_bar_toggled
- )
+ self._editor_code_widget.searchBarToggled.connect(self.search_bar_toggled)
- self._editor.trigger_find.connect(
- self._editor_code_widget.triggerFind
- )
+ self._editor.trigger_find.connect(self._editor_code_widget.triggerFind)
if filename:
if QFileInfo(filename).exists():
@@ -506,7 +568,7 @@ def __init__(self,
def set_file_path(self, path: str):
self._editor_code_widget.setFilePath(path)
- def file_path(self) -> Optional[str]:
+ def file_path(self) -> str | None:
return self._editor_code_widget.filePath()
def open_in_external_editor(self):
@@ -537,14 +599,14 @@ def close(self):
self.tab_widget._removeTab(self, tab2index=True)
def __getattr__(self, name):
- """ Forward all missing attribute requests to the editor."""
+ """Forward all missing attribute requests to the editor."""
try:
return super().__getattr__(name)
except AttributeError:
return getattr(self._editor, name)
def __setattr__(self, name, value):
- """ Forward all missing attribute requests to the editor."""
+ """Forward all missing attribute requests to the editor."""
try:
return super().__setattr__(name, value)
except AttributeError:
@@ -566,38 +628,45 @@ def __init__(self, console_widget: PythonConsoleWidget):
# Layout for top frame (restore tabs)
self.layoutTopFrame = QGridLayout(self)
self.layoutTopFrame.setContentsMargins(0, 0, 0, 0)
- spacerItem = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
+ spacerItem = QSpacerItem(
+ 20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
+ )
self.layoutTopFrame.addItem(spacerItem, 1, 0, 1, 1)
self.topFrame = QFrame(self)
- self.topFrame.setStyleSheet('background-color: rgb(255, 255, 230);')
+ self.topFrame.setStyleSheet("background-color: rgb(255, 255, 230);")
self.topFrame.setFrameShape(QFrame.Shape.StyledPanel)
self.topFrame.setMinimumHeight(24)
self.layoutTopFrame2 = QGridLayout(self.topFrame)
self.layoutTopFrame2.setContentsMargins(0, 0, 0, 0)
- label = QCoreApplication.translate("PythonConsole",
- "Click on button to restore all tabs from last session.")
+ label = QCoreApplication.translate(
+ "PythonConsole", "Click on button to restore all tabs from last session."
+ )
self.label = QLabel(label)
self.restoreTabsButton = QToolButton()
- toolTipRestore = QCoreApplication.translate("PythonConsole",
- "Restore tabs")
+ toolTipRestore = QCoreApplication.translate("PythonConsole", "Restore tabs")
self.restoreTabsButton.setToolTip(toolTipRestore)
- self.restoreTabsButton.setIcon(QgsApplication.getThemeIcon("console/iconRestoreTabsConsole.svg"))
+ self.restoreTabsButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconRestoreTabsConsole.svg")
+ )
self.restoreTabsButton.setIconSize(QSize(24, 24))
self.restoreTabsButton.setAutoRaise(True)
self.restoreTabsButton.setCursor(Qt.CursorShape.PointingHandCursor)
- self.restoreTabsButton.setStyleSheet('QToolButton:hover{border: none } \
- QToolButton:pressed{border: none}')
+ self.restoreTabsButton.setStyleSheet(
+ "QToolButton:hover{border: none } \
+ QToolButton:pressed{border: none}"
+ )
self.clButton = QToolButton()
- toolTipClose = QCoreApplication.translate("PythonConsole",
- "Close")
+ toolTipClose = QCoreApplication.translate("PythonConsole", "Close")
self.clButton.setToolTip(toolTipClose)
self.clButton.setIcon(QgsApplication.getThemeIcon("/mIconClose.svg"))
self.clButton.setIconSize(QSize(18, 18))
self.clButton.setCursor(Qt.CursorShape.PointingHandCursor)
- self.clButton.setStyleSheet('QToolButton:hover{border: none } \
- QToolButton:pressed{border: none}')
+ self.clButton.setStyleSheet(
+ "QToolButton:hover{border: none } \
+ QToolButton:pressed{border: none}"
+ )
self.clButton.setAutoRaise(True)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
@@ -612,7 +681,7 @@ def __init__(self, console_widget: PythonConsoleWidget):
self.clButton.clicked.connect(self.closeRestore)
# Fixes #7653
- if sys.platform != 'darwin':
+ if sys.platform != "darwin":
self.setDocumentMode(True)
self.setMovable(True)
@@ -624,10 +693,13 @@ def __init__(self, console_widget: PythonConsoleWidget):
self.fileTabMenu.aboutToShow.connect(self.showFileTabMenu)
self.fileTabMenu.triggered.connect(self.showFileTabMenuTriggered)
self.fileTabButton = QToolButton()
- txtToolTipMenuFile = QCoreApplication.translate("PythonConsole",
- "List all tabs")
+ txtToolTipMenuFile = QCoreApplication.translate(
+ "PythonConsole", "List all tabs"
+ )
self.fileTabButton.setToolTip(txtToolTipMenuFile)
- self.fileTabButton.setIcon(QgsApplication.getThemeIcon("console/iconFileTabsMenuConsole.svg"))
+ self.fileTabButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconFileTabsMenuConsole.svg")
+ )
self.fileTabButton.setIconSize(QSize(24, 24))
self.fileTabButton.setAutoRaise(True)
self.fileTabButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
@@ -638,26 +710,24 @@ def __init__(self, console_widget: PythonConsoleWidget):
# New Editor button
self.newTabButton = QToolButton()
- txtToolTipNewTab = QCoreApplication.translate("PythonConsole",
- "New Editor")
+ txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor")
self.newTabButton.setToolTip(txtToolTipNewTab)
self.newTabButton.setAutoRaise(True)
- self.newTabButton.setIcon(QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg"))
+ self.newTabButton.setIcon(
+ QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg")
+ )
self.newTabButton.setIconSize(QSize(24, 24))
self.setCornerWidget(self.newTabButton, Qt.Corner.TopLeftCorner)
self.newTabButton.clicked.connect(self.newTabEditor)
def _currentWidgetChanged(self, tab):
- if QgsSettings().value("pythonConsole/enableObjectInsp",
- False, type=bool):
+ if QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool):
self.listObject(tab)
self.changeLastDirPath(tab)
self.enableSaveIfModified(tab)
if self.currentWidget():
- self.search_bar_toggled.emit(
- self.currentWidget().search_bar_visible()
- )
+ self.search_bar_toggled.emit(self.currentWidget().search_bar_visible())
def toggle_search_bar(self, visible: bool):
"""
@@ -677,24 +747,26 @@ def contextMenuEvent(self, e):
menu.addSeparator()
menu.addAction(
QCoreApplication.translate("PythonConsole", "New Editor"),
- self.newTabEditor)
+ self.newTabEditor,
+ )
menu.addSeparator()
closeTabAction = menu.addAction(
- QCoreApplication.translate("PythonConsole", "Close Tab"),
- cW.close)
+ QCoreApplication.translate("PythonConsole", "Close Tab"), cW.close
+ )
closeAllTabAction = menu.addAction(
- QCoreApplication.translate("PythonConsole", "Close All"),
- self.closeAll)
+ QCoreApplication.translate("PythonConsole", "Close All"), self.closeAll
+ )
closeOthersTabAction = menu.addAction(
QCoreApplication.translate("PythonConsole", "Close Others"),
- self.closeOthers)
+ self.closeOthers,
+ )
menu.addSeparator()
saveAction = menu.addAction(
- QCoreApplication.translate("PythonConsole", "Save"),
- cW.save)
+ QCoreApplication.translate("PythonConsole", "Save"), cW.save
+ )
menu.addAction(
- QCoreApplication.translate("PythonConsole", "Save As"),
- self.saveAs)
+ QCoreApplication.translate("PythonConsole", "Save As"), self.saveAs
+ )
closeTabAction.setEnabled(False)
closeAllTabAction.setEnabled(False)
closeOthersTabAction.setEnabled(False)
@@ -718,7 +790,9 @@ def closeAll(self):
for i in range(countTab - 1, 0, -1):
self._removeTab(i)
- self.newTabEditor(tabName=QCoreApplication.translate("PythonConsole", "Untitled-0"))
+ self.newTabEditor(
+ tabName=QCoreApplication.translate("PythonConsole", "Untitled-0")
+ )
self._removeTab(0)
def saveAs(self):
@@ -734,31 +808,35 @@ def enableToolBarEditor(self, enable):
enable = False
self.console_widget.toolBarEditor.setEnabled(enable)
- def newTabEditor(self, tabName=None, filename: Optional[str] = None):
+ def newTabEditor(self, tabName=None, filename: str | None = None):
read_only = False
if filename:
read_only = not QFileInfo(filename).isWritable()
try:
- fn = codecs.open(filename, "rb", encoding='utf-8')
+ fn = codecs.open(filename, "rb", encoding="utf-8")
fn.read()
fn.close()
- except IOError as error:
- IOErrorTr = QCoreApplication.translate('PythonConsole',
- 'The file {0} could not be opened. Error: {1}\n').format(filename,
- error.strerror)
- print('## Error: ')
+ except OSError as error:
+ IOErrorTr = QCoreApplication.translate(
+ "PythonConsole", "The file {0} could not be opened. Error: {1}\n"
+ ).format(filename, error.strerror)
+ print("## Error: ")
sys.stderr.write(IOErrorTr)
return
nr = self.count()
if not tabName:
- tabName = QCoreApplication.translate('PythonConsole', 'Untitled-{0}').format(nr)
- tab = EditorTab(tab_widget=self,
- console_widget=self.console_widget,
- filename=filename,
- read_only=read_only)
- self.iconTab = QgsApplication.getThemeIcon('console/iconTabEditorConsole.svg')
- self.addTab(tab, self.iconTab, tabName + ' (ro)' if read_only else tabName)
+ tabName = QCoreApplication.translate(
+ "PythonConsole", "Untitled-{0}"
+ ).format(nr)
+ tab = EditorTab(
+ tab_widget=self,
+ console_widget=self.console_widget,
+ filename=filename,
+ read_only=read_only,
+ )
+ self.iconTab = QgsApplication.getThemeIcon("console/iconTabEditorConsole.svg")
+ self.addTab(tab, self.iconTab, tabName + " (ro)" if read_only else tabName)
self.setCurrentWidget(tab)
if filename:
self.setTabToolTip(self.currentIndex(), filename)
@@ -776,7 +854,7 @@ def _tab_search_bar_toggled(self, visible: bool):
def tabModified(self, tab, modified):
index = self.indexOf(tab)
s = self.tabText(index)
- self.setTabTitle(index, '*{}'.format(s) if modified else re.sub(r'^(\*)', '', s))
+ self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s))
self.console_widget.saveFileButton.setEnabled(modified)
def setTabTitle(self, tab, title):
@@ -787,25 +865,37 @@ def _removeTab(self, tab, tab2index=False):
tab = self.indexOf(tab)
editorTab = self.widget(tab)
if editorTab.isModified():
- txtSaveOnRemove = QCoreApplication.translate("PythonConsole",
- "Python Console: Save File")
- txtMsgSaveOnRemove = QCoreApplication.translate("PythonConsole",
- "The file '{0}' has been modified, save changes?").format(self.tabText(tab))
- res = QMessageBox.question(self, txtSaveOnRemove,
- txtMsgSaveOnRemove,
- QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Discard | QMessageBox.StandardButton.Cancel)
+ txtSaveOnRemove = QCoreApplication.translate(
+ "PythonConsole", "Python Console: Save File"
+ )
+ txtMsgSaveOnRemove = QCoreApplication.translate(
+ "PythonConsole",
+ "The file '{0}' has been modified, save changes?",
+ ).format(self.tabText(tab))
+ res = QMessageBox.question(
+ self,
+ txtSaveOnRemove,
+ txtMsgSaveOnRemove,
+ QMessageBox.StandardButton.Save
+ | QMessageBox.StandardButton.Discard
+ | QMessageBox.StandardButton.Cancel,
+ )
if res == QMessageBox.StandardButton.Cancel:
return
if res == QMessageBox.StandardButton.Save:
editorTab.save()
if editorTab.code_editor_widget.filePath():
- self.console_widget.updateTabListScript(editorTab.code_editor_widget.filePath(), action='remove')
+ self.console_widget.updateTabListScript(
+ editorTab.code_editor_widget.filePath(), action="remove"
+ )
self.removeTab(tab)
if self.count() < 1:
self.newTabEditor()
else:
if editorTab.code_editor_widget.filePath():
- self.console_widget.updateTabListScript(editorTab.code_editor_widget.filePath(), action='remove')
+ self.console_widget.updateTabListScript(
+ editorTab.code_editor_widget.filePath(), action="remove"
+ )
if self.count() <= 1:
self.removeTab(tab)
self.newTabEditor()
@@ -832,15 +922,16 @@ def restoreTabs(self):
for script in self.restoreTabList:
pathFile = script
if QFileInfo(pathFile).exists():
- tabName = pathFile.split('/')[-1]
+ tabName = pathFile.split("/")[-1]
self.newTabEditor(tabName, pathFile)
else:
- errOnRestore = QCoreApplication.translate("PythonConsole",
- "Unable to restore the file: \n{0}\n").format(pathFile)
- print('## Error: ')
+ errOnRestore = QCoreApplication.translate(
+ "PythonConsole", "Unable to restore the file: \n{0}\n"
+ ).format(pathFile)
+ print("## Error: ")
s = errOnRestore
sys.stderr.write(s)
- self.console_widget.updateTabListScript(pathFile, action='remove')
+ self.console_widget.updateTabListScript(pathFile, action="remove")
if self.count() < 1:
self.newTabEditor(filename=None)
self.topFrame.close()
@@ -856,7 +947,9 @@ def closeRestore(self):
def showFileTabMenu(self):
self.fileTabMenu.clear()
for index in range(self.count()):
- action = self.fileTabMenu.addAction(self.tabIcon(index), self.tabText(index))
+ action = self.fileTabMenu.addAction(
+ self.tabIcon(index), self.tabText(index)
+ )
action.setData(index)
def showFileTabMenuTriggered(self, action):
@@ -883,11 +976,15 @@ def listObject(self, tab):
dictObject = {}
readModule = pyclbr.readmodule(module)
readModuleFunction = pyclbr.readmodule_ex(module)
- for name, class_data in sorted(list(readModule.items()), key=lambda x: x[1].lineno):
- if os.path.normpath(class_data.file) == os.path.normpath(tabWidget.file_path()):
+ for name, class_data in sorted(
+ list(readModule.items()), key=lambda x: x[1].lineno
+ ):
+ if os.path.normpath(class_data.file) == os.path.normpath(
+ tabWidget.file_path()
+ ):
superClassName = []
for superClass in class_data.super:
- if superClass == 'object':
+ if superClass == "object":
continue
if isinstance(superClass, str):
superClassName.append(superClass)
@@ -895,56 +992,76 @@ def listObject(self, tab):
superClassName.append(superClass.name)
classItem = QTreeWidgetItem()
if superClassName:
- super = ', '.join([i for i in superClassName])
- classItem.setText(0, name + ' [' + super + ']')
- classItem.setToolTip(0, name + ' [' + super + ']')
+ super = ", ".join([i for i in superClassName])
+ classItem.setText(0, name + " [" + super + "]")
+ classItem.setToolTip(0, name + " [" + super + "]")
else:
classItem.setText(0, name)
classItem.setToolTip(0, name)
- if sys.platform.startswith('win'):
+ if sys.platform.startswith("win"):
classItem.setSizeHint(0, QSize(18, 18))
classItem.setText(1, str(class_data.lineno))
- iconClass = QgsApplication.getThemeIcon("console/iconClassTreeWidgetConsole.svg")
+ iconClass = QgsApplication.getThemeIcon(
+ "console/iconClassTreeWidgetConsole.svg"
+ )
classItem.setIcon(0, iconClass)
dictObject[name] = class_data.lineno
- for meth, lineno in sorted(list(class_data.methods.items()), key=itemgetter(1)):
+ for meth, lineno in sorted(
+ list(class_data.methods.items()), key=itemgetter(1)
+ ):
methodItem = QTreeWidgetItem()
- methodItem.setText(0, meth + ' ')
+ methodItem.setText(0, meth + " ")
methodItem.setText(1, str(lineno))
methodItem.setToolTip(0, meth)
- iconMeth = QgsApplication.getThemeIcon("console/iconMethodTreeWidgetConsole.svg")
+ iconMeth = QgsApplication.getThemeIcon(
+ "console/iconMethodTreeWidgetConsole.svg"
+ )
methodItem.setIcon(0, iconMeth)
- if sys.platform.startswith('win'):
+ if sys.platform.startswith("win"):
methodItem.setSizeHint(0, QSize(18, 18))
classItem.addChild(methodItem)
dictObject[meth] = lineno
- self.console_widget.listClassMethod.addTopLevelItem(classItem)
- for func_name, data in sorted(list(readModuleFunction.items()), key=lambda x: x[1].lineno):
- if isinstance(data, pyclbr.Function) and \
- os.path.normpath(data.file) == os.path.normpath(tabWidget.file_path()):
+ self.console_widget.listClassMethod.addTopLevelItem(
+ classItem
+ )
+ for func_name, data in sorted(
+ list(readModuleFunction.items()), key=lambda x: x[1].lineno
+ ):
+ if isinstance(data, pyclbr.Function) and os.path.normpath(
+ data.file
+ ) == os.path.normpath(tabWidget.file_path()):
funcItem = QTreeWidgetItem()
- funcItem.setText(0, func_name + ' ')
+ funcItem.setText(0, func_name + " ")
funcItem.setText(1, str(data.lineno))
funcItem.setToolTip(0, func_name)
- iconFunc = QgsApplication.getThemeIcon("console/iconFunctionTreeWidgetConsole.svg")
+ iconFunc = QgsApplication.getThemeIcon(
+ "console/iconFunctionTreeWidgetConsole.svg"
+ )
funcItem.setIcon(0, iconFunc)
- if sys.platform.startswith('win'):
+ if sys.platform.startswith("win"):
funcItem.setSizeHint(0, QSize(18, 18))
dictObject[func_name] = data.lineno
- self.console_widget.listClassMethod.addTopLevelItem(funcItem)
+ self.console_widget.listClassMethod.addTopLevelItem(
+ funcItem
+ )
if found:
sys.path.remove(pathFile)
except:
msgItem = QTreeWidgetItem()
- msgItem.setText(0, QCoreApplication.translate("PythonConsole", "Check Syntax"))
- msgItem.setText(1, 'syntaxError')
- iconWarning = QgsApplication.getThemeIcon("console/iconSyntaxErrorConsole.svg")
+ msgItem.setText(
+ 0, QCoreApplication.translate("PythonConsole", "Check Syntax")
+ )
+ msgItem.setText(1, "syntaxError")
+ iconWarning = QgsApplication.getThemeIcon(
+ "console/iconSyntaxErrorConsole.svg"
+ )
msgItem.setIcon(0, iconWarning)
self.console_widget.listClassMethod.addTopLevelItem(msgItem)
def refreshSettingsEditor(self):
- objInspectorEnabled = QgsSettings().value("pythonConsole/enableObjectInsp",
- False, type=bool)
+ objInspectorEnabled = QgsSettings().value(
+ "pythonConsole/enableObjectInsp", False, type=bool
+ )
listObj = self.console_widget.objectListButton
if self.console_widget.listClassMethod.isVisible():
listObj.setChecked(objInspectorEnabled)
@@ -958,8 +1075,10 @@ def refreshSettingsEditor(self):
def changeLastDirPath(self, tab):
tabWidget = self.widget(tab)
if tabWidget and tabWidget.file_path():
- QgsSettings().setValue("pythonConsole/lastDirPath",
- Path(tabWidget.file_path()).parent.as_posix())
+ QgsSettings().setValue(
+ "pythonConsole/lastDirPath",
+ Path(tabWidget.file_path()).parent.as_posix(),
+ )
def showMessage(self, text, level=Qgis.MessageLevel.Info, timeout=-1, title=""):
currWidget = self.currentWidget()
diff --git a/python/console/console_output.py b/python/console/console_output.py
index 61ea81b599d5..550501ec9280 100644
--- a/python/console/console_output.py
+++ b/python/console/console_output.py
@@ -17,15 +17,32 @@
***************************************************************************/
Some portions of code were taken from https://code.google.com/p/pydee/
"""
+
from __future__ import annotations
import sys
from typing import TYPE_CHECKING
from qgis.PyQt import sip
-from qgis.PyQt.QtCore import Qt, QCoreApplication, QThread, QMetaObject, Q_ARG, QObject, pyqtSlot
+from qgis.PyQt.QtCore import (
+ Qt,
+ QCoreApplication,
+ QThread,
+ QMetaObject,
+ Q_ARG,
+ QObject,
+ pyqtSlot,
+)
from qgis.PyQt.QtGui import QColor, QKeySequence
-from qgis.PyQt.QtWidgets import QAction, QGridLayout, QSpacerItem, QSizePolicy, QShortcut, QMenu, QApplication
+from qgis.PyQt.QtWidgets import (
+ QAction,
+ QGridLayout,
+ QSpacerItem,
+ QSizePolicy,
+ QShortcut,
+ QMenu,
+ QApplication,
+)
from qgis.PyQt.Qsci import QsciScintilla
from qgis.core import Qgis, QgsApplication, QgsSettings
from qgis.gui import QgsMessageBar, QgsCodeEditorPython
@@ -57,19 +74,33 @@ def write(self, m):
# This manage the case when console is called from another thread
if QThread.currentThread() != QCoreApplication.instance().thread():
- QMetaObject.invokeMethod(self, "write", Qt.ConnectionType.QueuedConnection, Q_ARG(str, m))
+ QMetaObject.invokeMethod(
+ self, "write", Qt.ConnectionType.QueuedConnection, Q_ARG(str, m)
+ )
return
if self.style == "_traceback":
# Show errors in red
- stderrColor = QColor(QgsSettings().value("pythonConsole/stderrFontColor", QColor(self.ERROR_COLOR)))
- self.sO.SendScintilla(QsciScintilla.SCI_STYLESETFORE, self.ERROR_STYLE_INDEX, stderrColor)
- self.sO.SendScintilla(QsciScintilla.SCI_STYLESETITALIC, self.ERROR_STYLE_INDEX, True)
- self.sO.SendScintilla(QsciScintilla.SCI_STYLESETBOLD, self.ERROR_STYLE_INDEX, True)
+ stderrColor = QColor(
+ QgsSettings().value(
+ "pythonConsole/stderrFontColor", QColor(self.ERROR_COLOR)
+ )
+ )
+ self.sO.SendScintilla(
+ QsciScintilla.SCI_STYLESETFORE, self.ERROR_STYLE_INDEX, stderrColor
+ )
+ self.sO.SendScintilla(
+ QsciScintilla.SCI_STYLESETITALIC, self.ERROR_STYLE_INDEX, True
+ )
+ self.sO.SendScintilla(
+ QsciScintilla.SCI_STYLESETBOLD, self.ERROR_STYLE_INDEX, True
+ )
pos = self.sO.linearPosition()
self.sO.SendScintilla(QsciScintilla.SCI_STARTSTYLING, pos, 0)
self.sO.append(m)
- self.sO.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(m), self.ERROR_STYLE_INDEX)
+ self.sO.SendScintilla(
+ QsciScintilla.SCI_SETSTYLING, len(m), self.ERROR_STYLE_INDEX
+ )
else:
self.sO.append(m)
@@ -93,7 +124,9 @@ def isatty(self):
return False
-FULL_HELP_TEXT = QCoreApplication.translate("PythonConsole", """QGIS Python Console
+FULL_HELP_TEXT = QCoreApplication.translate(
+ "PythonConsole",
+ """QGIS Python Console
======================================
The console is a Python interpreter that allows you to execute python commands.
@@ -121,14 +154,15 @@ def isatty(self):
!ping www.qgis.org: Ping the QGIS website
!pip install black: install black python formatter using pip (if available)
- ?: Show this help
-""")
+""",
+)
class ShellOutputScintilla(QgsCodeEditorPython):
- def __init__(self,
- console_widget: PythonConsoleWidget,
- shell_editor: ShellScintilla):
+ def __init__(
+ self, console_widget: PythonConsoleWidget, shell_editor: ShellScintilla
+ ):
super().__init__(console_widget)
self.console_widget: PythonConsoleWidget = console_widget
self.shell_editor: ShellScintilla = shell_editor
@@ -136,7 +170,9 @@ def __init__(self,
# Creates layout for message bar
self.layout = QGridLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
- spacerItem = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
+ spacerItem = QSpacerItem(
+ 20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
+ )
self.layout.addItem(spacerItem, 1, 0, 1, 1)
# messageBar instance
self.infoBar = QgsMessageBar()
@@ -179,20 +215,22 @@ def on_app_exit(self):
sys.stderr = self._old_stderr
def insertInitText(self):
- txtInit = QCoreApplication.translate("PythonConsole",
- "Python Console\n"
- "Use iface to access QGIS API interface or type '?' for more info\n"
- "Security warning: typing commands from an untrusted source can harm your computer")
+ txtInit = QCoreApplication.translate(
+ "PythonConsole",
+ "Python Console\n"
+ "Use iface to access QGIS API interface or type '?' for more info\n"
+ "Security warning: typing commands from an untrusted source can harm your computer",
+ )
- txtInit = '\n'.join(['# ' + line for line in txtInit.split('\n')])
+ txtInit = "\n".join(["# " + line for line in txtInit.split("\n")])
# some translation string for the console header ends without '\n'
# and the first command in console will be appended at the header text.
# The following code add a '\n' at the end of the string if not present.
- if txtInit.endswith('\n'):
+ if txtInit.endswith("\n"):
self.setText(txtInit)
else:
- self.setText(txtInit + '\n')
+ self.setText(txtInit + "\n")
def insertHelp(self):
self.append(FULL_HELP_TEXT)
@@ -211,37 +249,48 @@ def refreshSettingsOutput(self):
self.setCaretWidth(0) # NO (blinking) caret in the output
def clearConsole(self):
- self.setText('')
+ self.setText("")
self.insertInitText()
self.shell_editor.setFocus()
def contextMenuEvent(self, e):
menu = QMenu(self)
- menu.addAction(QgsApplication.getThemeIcon("console/iconHideToolConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"),
- self.hideToolBar)
+ menu.addAction(
+ QgsApplication.getThemeIcon("console/iconHideToolConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"),
+ self.hideToolBar,
+ )
menu.addSeparator()
showEditorAction = menu.addAction(
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"),
QCoreApplication.translate("PythonConsole", "Show Editor"),
- self.showEditor)
+ self.showEditor,
+ )
menu.addSeparator()
- runAction = QAction(QgsApplication.getThemeIcon("console/mIconRunConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Enter Selected"),
- menu)
+ runAction = QAction(
+ QgsApplication.getThemeIcon("console/mIconRunConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Enter Selected"),
+ menu,
+ )
runAction.triggered.connect(self.enteredSelected)
runAction.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_E))
menu.addAction(runAction)
- clearAction = QAction(QgsApplication.getThemeIcon("console/iconClearConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Clear Console"),
- menu)
+ clearAction = QAction(
+ QgsApplication.getThemeIcon("console/iconClearConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Clear Console"),
+ menu,
+ )
clearAction.triggered.connect(self.clearConsole)
menu.addAction(clearAction)
- pyQGISHelpAction = QAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Search Selection in PyQGIS Documentation"),
- menu)
+ pyQGISHelpAction = QAction(
+ QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
+ QCoreApplication.translate(
+ "PythonConsole", "Search Selection in PyQGIS Documentation"
+ ),
+ menu,
+ )
pyQGISHelpAction.triggered.connect(self.searchSelectedTextInPyQGISDocs)
menu.addAction(pyQGISHelpAction)
@@ -249,22 +298,25 @@ def contextMenuEvent(self, e):
copyAction = QAction(
QgsApplication.getThemeIcon("mActionEditCopy.svg"),
QCoreApplication.translate("PythonConsole", "Copy"),
- menu)
+ menu,
+ )
copyAction.triggered.connect(self.copy)
copyAction.setShortcut(QKeySequence.StandardKey.Copy)
menu.addAction(copyAction)
selectAllAction = QAction(
- QCoreApplication.translate("PythonConsole", "Select All"),
- menu)
+ QCoreApplication.translate("PythonConsole", "Select All"), menu
+ )
selectAllAction.triggered.connect(self.selectAll)
selectAllAction.setShortcut(QKeySequence.StandardKey.SelectAll)
menu.addAction(selectAllAction)
menu.addSeparator()
- settings_action = QAction(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Options…"),
- menu)
+ settings_action = QAction(
+ QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
+ QCoreApplication.translate("PythonConsole", "Options…"),
+ menu,
+ )
settings_action.triggered.connect(self.console_widget.openSettings)
menu.addAction(settings_action)
@@ -278,7 +330,7 @@ def contextMenuEvent(self, e):
runAction.setEnabled(True)
copyAction.setEnabled(True)
pyQGISHelpAction.setEnabled(True)
- if not self.text(3) == '':
+ if not self.text(3) == "":
selectAllAction.setEnabled(True)
clearAction.setEnabled(True)
if self.console_widget.tabEditorWidget.isVisible():
@@ -301,7 +353,9 @@ def copy(self):
"""Copy text to clipboard... or keyboard interrupt"""
if self.hasSelectedText():
text = self.selectedText()
- text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts
+ text = (
+ text.replace(">>> ", "").replace("... ", "").strip()
+ ) # removing prompts
QApplication.clipboard().setText(text)
else:
raise KeyboardInterrupt
diff --git a/python/console/console_sci.py b/python/console/console_sci.py
index ba0688c8d9df..92441349d720 100644
--- a/python/console/console_sci.py
+++ b/python/console/console_sci.py
@@ -17,6 +17,7 @@
***************************************************************************/
Some portions of code were taken from https://code.google.com/p/pydee/
"""
+
from __future__ import annotations
import code
@@ -24,10 +25,7 @@
import re
import sys
import traceback
-from typing import (
- Optional,
- TYPE_CHECKING
-)
+from typing import Optional, TYPE_CHECKING
from pathlib import Path
from tempfile import NamedTemporaryFile
@@ -35,18 +33,11 @@
from qgis.PyQt.QtCore import Qt, QCoreApplication
from qgis.PyQt.QtGui import QKeySequence, QFontMetrics, QClipboard
from qgis.PyQt.QtWidgets import QShortcut, QApplication
-from qgis.core import (
- QgsApplication,
- Qgis,
- QgsProcessingUtils
-)
-from qgis.gui import (
- QgsCodeEditorPython,
- QgsCodeEditor,
- QgsCodeInterpreter
-)
+from qgis.core import QgsApplication, Qgis, QgsProcessingUtils
+from qgis.gui import QgsCodeEditorPython, QgsCodeEditor, QgsCodeInterpreter
from .process_wrapper import ProcessWrapper
+
if TYPE_CHECKING:
from .console import PythonConsoleWidget
@@ -77,7 +68,6 @@
"from qgis.PyQt.QtWidgets import *",
"from qgis.PyQt.QtNetwork import *",
"from qgis.PyQt.QtXml import *",
-
r"""
def __parse_object(object=None):
if not object:
@@ -141,7 +131,7 @@ def _pyqgis(object=None):
elif api[0] == 'qt':
qtversion = '.'.join(qVersion().split(".")[:2])
webbrowser.open(f"https://doc.qt.io/qt-{qtversion}/{api[2].lower()}.html")
-"""
+""",
]
@@ -199,7 +189,9 @@ def execCommandImpl(self, cmd, show_input=True):
# Use a temporary file to communicate the result to the inner interpreter
tmp = Path(NamedTemporaryFile(delete=False).name)
tmp.write_text(res, encoding="utf-8")
- self.runsource(f'{varname} = Path("{tmp}").read_text(encoding="utf-8").split("\\n")')
+ self.runsource(
+ f'{varname} = Path("{tmp}").read_text(encoding="utf-8").split("\\n")'
+ )
tmp.unlink()
self.sub_process = None
return 0
@@ -218,19 +210,27 @@ def execCommandImpl(self, cmd, show_input=True):
res = 0
import webbrowser
- version = 'master' if 'master' in Qgis.QGIS_VERSION.lower() else \
- re.findall(r'^\d.[0-9]*', Qgis.QGIS_VERSION)[0]
+
+ version = (
+ "master"
+ if "master" in Qgis.QGIS_VERSION.lower()
+ else re.findall(r"^\d.[0-9]*", Qgis.QGIS_VERSION)[0]
+ )
if cmd == "?":
self.shell.console_widget.shell_output.insertHelp()
- elif cmd == '_pyqgis':
- webbrowser.open("https://qgis.org/pyqgis/{}".format(version))
- elif cmd == '_api':
- webbrowser.open("https://qgis.org/api/{}".format('' if version == 'master' else version))
- elif cmd == '_cookbook':
+ elif cmd == "_pyqgis":
+ webbrowser.open(f"https://qgis.org/pyqgis/{version}")
+ elif cmd == "_api":
+ webbrowser.open(
+ "https://qgis.org/api/{}".format("" if version == "master" else version)
+ )
+ elif cmd == "_cookbook":
webbrowser.open(
"https://docs.qgis.org/{}/en/docs/pyqgis_developer_cookbook/".format(
- 'testing' if version == 'master' else version))
+ "testing" if version == "master" else version
+ )
+ )
else:
self.buffer.append(cmd)
src = "\n".join(self.buffer)
@@ -244,20 +244,21 @@ def writeCMD(self, txt):
if sys.stdout:
sys.stdout.fire_keyboard_interrupt = False
if len(txt) > 0:
- sys.stdout.write(f'{self.promptForState()} {txt}\n')
+ sys.stdout.write(f"{self.promptForState()} {txt}\n")
- def runsource(self, source, filename='', symbol='single'):
+ def runsource(self, source, filename="", symbol="single"):
if sys.stdout:
sys.stdout.fire_keyboard_interrupt = False
hook = sys.excepthook
try:
+
def excepthook(etype, value, tb):
self.write("".join(traceback.format_exception(etype, value, tb)))
sys.excepthook = excepthook
- return super(PythonInterpreter, self).runsource(source, filename, symbol)
+ return super().runsource(source, filename, symbol)
finally:
sys.excepthook = hook
@@ -287,33 +288,45 @@ def __init__(self, console_widget: PythonConsoleWidget):
# We set the ImmediatelyUpdateHistory flag here, as users can easily
# crash QGIS by entering a Python command, and we don't want the
# history leading to the crash lost...
- super().__init__(console_widget, [], QgsCodeEditor.Mode.CommandInput,
- flags=QgsCodeEditor.Flags(QgsCodeEditor.Flag.CodeFolding | QgsCodeEditor.Flag.ImmediatelyUpdateHistory))
+ super().__init__(
+ console_widget,
+ [],
+ QgsCodeEditor.Mode.CommandInput,
+ flags=QgsCodeEditor.Flags(
+ QgsCodeEditor.Flag.CodeFolding
+ | QgsCodeEditor.Flag.ImmediatelyUpdateHistory
+ ),
+ )
self.console_widget: PythonConsoleWidget = console_widget
self._interpreter = PythonInterpreter(shell=self)
self.setInterpreter(self._interpreter)
- self.opening = ['(', '{', '[', "'", '"']
- self.closing = [')', '}', ']', "'", '"']
+ self.opening = ["(", "{", "[", "'", '"']
+ self.closing = [")", "}", "]", "'", '"']
self.setHistoryFilePath(
- os.path.join(QgsApplication.qgisSettingsDirPath(), "console_history.txt"))
+ os.path.join(QgsApplication.qgisSettingsDirPath(), "console_history.txt")
+ )
self.refreshSettingsShell()
# Disable command key
ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("T") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("D") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl + shift)
# New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete
- self.newShortcutCSS = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_Space), self)
- self.newShortcutCAS = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.ALT | Qt.Key.Key_Space), self)
+ self.newShortcutCSS = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_Space), self
+ )
+ self.newShortcutCAS = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.ALT | Qt.Key.Key_Space), self
+ )
self.newShortcutCSS.setContext(Qt.ShortcutContext.WidgetShortcut)
self.newShortcutCAS.setContext(Qt.ShortcutContext.WidgetShortcut)
self.newShortcutCAS.activated.connect(self.autoComplete)
@@ -336,18 +349,25 @@ def refreshSettingsShell(self):
self._setMinimumHeight()
def on_session_history_cleared(self):
- msgText = QCoreApplication.translate('PythonConsole',
- 'Session history cleared successfully.')
+ msgText = QCoreApplication.translate(
+ "PythonConsole", "Session history cleared successfully."
+ )
self.console_widget.callWidgetMessageBar(msgText)
def on_persistent_history_cleared(self):
- msgText = QCoreApplication.translate('PythonConsole',
- 'History cleared successfully.')
+ msgText = QCoreApplication.translate(
+ "PythonConsole", "History cleared successfully."
+ )
self.console_widget.callWidgetMessageBar(msgText)
def keyPressEvent(self, e):
- if e.modifiers() & (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.MetaModifier) and e.key() == Qt.Key.Key_C and not self.hasSelectedText():
+ if (
+ e.modifiers()
+ & (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.MetaModifier)
+ and e.key() == Qt.Key.Key_C
+ and not self.hasSelectedText()
+ ):
if self._interpreter.sub_process:
sys.stderr.write("Terminate child process\n")
self._interpreter.sub_process.kill()
@@ -436,25 +456,40 @@ def write(self, txt):
if sys.stderr:
sys.stderr.write(txt)
- def runFile(self, filename, override_file_name: Optional[str] = None):
+ def runFile(self, filename, override_file_name: str | None = None):
filename = filename.replace("\\", "/")
dirname = os.path.dirname(filename)
# Append the directory of the file to the path and set __file__ to the filename
- self._interpreter.execCommandImpl("sys.path.append({0})".format(
- QgsProcessingUtils.stringToPythonLiteral(dirname)), False)
- self._interpreter.execCommandImpl("__file__ = {0}".format(
- QgsProcessingUtils.stringToPythonLiteral(filename)), False)
+ self._interpreter.execCommandImpl(
+ "sys.path.append({})".format(
+ QgsProcessingUtils.stringToPythonLiteral(dirname)
+ ),
+ False,
+ )
+ self._interpreter.execCommandImpl(
+ f"__file__ = {QgsProcessingUtils.stringToPythonLiteral(filename)}",
+ False,
+ )
try:
# Run the file
- self.runCommand("exec(compile(Path({0}).read_text(), {1}, 'exec'))".format(
- QgsProcessingUtils.stringToPythonLiteral(filename),
- QgsProcessingUtils.stringToPythonLiteral(override_file_name or filename)),
- skipHistory=True)
+ self.runCommand(
+ "exec(compile(Path({}).read_text(), {}, 'exec'))".format(
+ QgsProcessingUtils.stringToPythonLiteral(filename),
+ QgsProcessingUtils.stringToPythonLiteral(
+ override_file_name or filename
+ ),
+ ),
+ skipHistory=True,
+ )
finally:
# Remove the directory from the path and delete the __file__ variable
self._interpreter.execCommandImpl("del __file__", False)
- self._interpreter.execCommandImpl("sys.path.remove({0})".format(
- QgsProcessingUtils.stringToPythonLiteral(dirname)), False)
+ self._interpreter.execCommandImpl(
+ "sys.path.remove({})".format(
+ QgsProcessingUtils.stringToPythonLiteral(dirname)
+ ),
+ False,
+ )
diff --git a/python/console/console_settings.py b/python/console/console_settings.py
index a34e0adc259d..f9b154c3ac21 100644
--- a/python/console/console_settings.py
+++ b/python/console/console_settings.py
@@ -22,7 +22,13 @@
from qgis.PyQt import uic
from qgis.PyQt.QtCore import QCoreApplication, QUrl
-from qgis.PyQt.QtWidgets import QWidget, QFileDialog, QMessageBox, QTableWidgetItem, QHBoxLayout
+from qgis.PyQt.QtWidgets import (
+ QWidget,
+ QFileDialog,
+ QMessageBox,
+ QTableWidgetItem,
+ QHBoxLayout,
+)
from qgis.PyQt.QtGui import QIcon, QDesktopServices
from qgis.core import QgsSettings, QgsApplication, QgsSettingsTree
@@ -30,7 +36,9 @@
from .console_compile_apis import PrepareAPIDialog
-Ui_SettingsDialogPythonConsole, _ = uic.loadUiType(Path(__file__).parent / 'console_settings.ui')
+Ui_SettingsDialogPythonConsole, _ = uic.loadUiType(
+ Path(__file__).parent / "console_settings.ui"
+)
class ConsoleOptionsFactory(QgsOptionsWidgetFactory):
@@ -39,10 +47,10 @@ def __init__(self):
super(QgsOptionsWidgetFactory, self).__init__()
def icon(self):
- return QgsApplication.getThemeIcon('/console/mIconRunConsole.svg')
+ return QgsApplication.getThemeIcon("/console/mIconRunConsole.svg")
def path(self):
- return ['ide']
+ return ["ide"]
def createWidget(self, parent):
return ConsoleOptionsPage(parent)
@@ -51,28 +59,31 @@ def createWidget(self, parent):
class ConsoleOptionsPage(QgsOptionsPageWidget):
def __init__(self, parent):
- super(ConsoleOptionsPage, self).__init__(parent)
+ super().__init__(parent)
self.options_widget = ConsoleOptionsWidget(parent)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.setMargin(0)
self.setLayout(layout)
layout.addWidget(self.options_widget)
- self.setObjectName('consoleOptions')
+ self.setObjectName("consoleOptions")
def apply(self):
self.options_widget.accept()
def helpKey(self):
- return 'plugins/python_console.html'
+ return "plugins/python_console.html"
class ConsoleOptionsWidget(QWidget, Ui_SettingsDialogPythonConsole):
def __init__(self, parent):
super().__init__(parent)
- self.setWindowTitle(QCoreApplication.translate(
- "SettingsDialogPythonConsole", "Python Console Settings"))
+ self.setWindowTitle(
+ QCoreApplication.translate(
+ "SettingsDialogPythonConsole", "Python Console Settings"
+ )
+ )
self.parent = parent
self.setupUi(self)
@@ -89,9 +100,13 @@ def __init__(self, parent):
self.initialCheck()
self.addAPIpath.setIcon(QIcon(":/images/themes/default/symbologyAdd.svg"))
- self.addAPIpath.setToolTip(QCoreApplication.translate("PythonConsole", "Add API path"))
+ self.addAPIpath.setToolTip(
+ QCoreApplication.translate("PythonConsole", "Add API path")
+ )
self.removeAPIpath.setIcon(QIcon(":/images/themes/default/symbologyRemove.svg"))
- self.removeAPIpath.setToolTip(QCoreApplication.translate("PythonConsole", "Remove API path"))
+ self.removeAPIpath.setToolTip(
+ QCoreApplication.translate("PythonConsole", "Remove API path")
+ )
self.preloadAPI.stateChanged.connect(self.initialCheck)
self.addAPIpath.clicked.connect(self.loadAPIFile)
@@ -117,7 +132,8 @@ def loadAPIFile(self):
settings = QgsSettings()
lastDirPath = settings.value("pythonConsole/lastDirAPIPath", "", type=str)
fileAPI, selected_filter = QFileDialog.getOpenFileName(
- self, "Open API File", lastDirPath, "API file (*.api)")
+ self, "Open API File", lastDirPath, "API file (*.api)"
+ )
if fileAPI:
self.addAPI(fileAPI)
settings.setValue("pythonConsole/lastDirAPIPath", fileAPI)
@@ -125,17 +141,17 @@ def loadAPIFile(self):
def _prepareAPI(self):
if self.tableWidget.rowCount() != 0:
pap_file, filter = QFileDialog().getSaveFileName(
- self,
- "",
- '*.pap',
- "Prepared APIs file (*.pap)")
+ self, "", "*.pap", "Prepared APIs file (*.pap)"
+ )
else:
QMessageBox.information(
- self, self.tr("Warning!"),
- self.tr('You need to add some APIs file in order to compile'))
+ self,
+ self.tr("Warning!"),
+ self.tr("You need to add some APIs file in order to compile"),
+ )
return
if pap_file:
- api_lexer = 'QsciLexerPython'
+ api_lexer = "QsciLexerPython"
api_files = []
count = self.tableWidget.rowCount()
for i in range(0, count):
@@ -148,18 +164,24 @@ def _prepareAPI(self):
self.lineEdit.setText(pap_file)
def accept(self):
- if not self.preloadAPI.isChecked() and \
- not self.groupBoxPreparedAPI.isChecked():
+ if not self.preloadAPI.isChecked() and not self.groupBoxPreparedAPI.isChecked():
if self.tableWidget.rowCount() == 0:
QMessageBox.information(
- self, self.tr("Warning!"),
- self.tr('Please specify API file or check "Use preloaded API files"'))
+ self,
+ self.tr("Warning!"),
+ self.tr(
+ 'Please specify API file or check "Use preloaded API files"'
+ ),
+ )
return
- if self.groupBoxPreparedAPI.isChecked() and \
- not self.lineEdit.text():
+ if self.groupBoxPreparedAPI.isChecked() and not self.lineEdit.text():
QMessageBox.information(
- self, self.tr("Warning!"),
- QCoreApplication.translate('optionsDialog', 'The APIs file was not compiled, click on "Compile APIs…"')
+ self,
+ self.tr("Warning!"),
+ QCoreApplication.translate(
+ "optionsDialog",
+ 'The APIs file was not compiled, click on "Compile APIs…"',
+ ),
)
return
self.saveSettings()
@@ -184,46 +206,77 @@ def removeAPI(self):
def saveSettings(self):
settings = QgsSettings()
settings.setValue("pythonConsole/preloadAPI", self.preloadAPI.isChecked())
- settings.setValue("pythonConsole/autoSaveScript", self.autoSaveScript.isChecked())
+ settings.setValue(
+ "pythonConsole/autoSaveScript", self.autoSaveScript.isChecked()
+ )
for i in range(0, self.tableWidget.rowCount()):
text = self.tableWidget.item(i, 1).text()
self.listPath.append(text)
settings.setValue("pythonConsole/userAPI", self.listPath)
- settings.setValue("pythonConsole/autoCompThreshold", self.autoCompThreshold.value())
- settings.setValue("pythonConsole/autoCompleteEnabled", self.groupBoxAutoCompletion.isChecked())
+ settings.setValue(
+ "pythonConsole/autoCompThreshold", self.autoCompThreshold.value()
+ )
+ settings.setValue(
+ "pythonConsole/autoCompleteEnabled", self.groupBoxAutoCompletion.isChecked()
+ )
- settings.setValue("pythonConsole/usePreparedAPIFile", self.groupBoxPreparedAPI.isChecked())
+ settings.setValue(
+ "pythonConsole/usePreparedAPIFile", self.groupBoxPreparedAPI.isChecked()
+ )
settings.setValue("pythonConsole/preparedAPIFile", self.lineEdit.text())
if self.autoCompFromAPI.isChecked():
- settings.setValue("pythonConsole/autoCompleteSource", 'fromAPI')
+ settings.setValue("pythonConsole/autoCompleteSource", "fromAPI")
elif self.autoCompFromDoc.isChecked():
- settings.setValue("pythonConsole/autoCompleteSource", 'fromDoc')
+ settings.setValue("pythonConsole/autoCompleteSource", "fromDoc")
elif self.autoCompFromDocAPI.isChecked():
- settings.setValue("pythonConsole/autoCompleteSource", 'fromDocAPI')
+ settings.setValue("pythonConsole/autoCompleteSource", "fromDocAPI")
- settings.setValue("pythonConsole/enableObjectInsp", self.enableObjectInspector.isChecked())
- settings.setValue("pythonConsole/autoCloseBracket", self.autoCloseBracket.isChecked())
+ settings.setValue(
+ "pythonConsole/enableObjectInsp", self.enableObjectInspector.isChecked()
+ )
+ settings.setValue(
+ "pythonConsole/autoCloseBracket", self.autoCloseBracket.isChecked()
+ )
settings.setValue("pythonConsole/autoSurround", self.autoSurround.isChecked())
- settings.setValue("pythonConsole/autoInsertImport", self.autoInsertImport.isChecked())
+ settings.setValue(
+ "pythonConsole/autoInsertImport", self.autoInsertImport.isChecked()
+ )
settings.setValue("pythonConsole/formatOnSave", self.formatOnSave.isChecked())
- pythonSettingsTreeNode = QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")
- pythonSettingsTreeNode.childSetting("sort-imports").setValue(self.sortImports.isChecked())
- pythonSettingsTreeNode.childSetting("formatter").setValue(self.formatter.currentText())
- pythonSettingsTreeNode.childSetting("autopep8-level").setValue(self.autopep8Level.value())
- pythonSettingsTreeNode.childSetting("black-normalize-quotes").setValue(self.blackNormalizeQuotes.isChecked())
- pythonSettingsTreeNode.childSetting("max-line-length").setValue(self.maxLineLength.value())
- pythonSettingsTreeNode.childSetting('external-editor').setValue(
- self.externalEditor.text())
+ pythonSettingsTreeNode = (
+ QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")
+ )
+ pythonSettingsTreeNode.childSetting("sort-imports").setValue(
+ self.sortImports.isChecked()
+ )
+ pythonSettingsTreeNode.childSetting("formatter").setValue(
+ self.formatter.currentText()
+ )
+ pythonSettingsTreeNode.childSetting("autopep8-level").setValue(
+ self.autopep8Level.value()
+ )
+ pythonSettingsTreeNode.childSetting("black-normalize-quotes").setValue(
+ self.blackNormalizeQuotes.isChecked()
+ )
+ pythonSettingsTreeNode.childSetting("max-line-length").setValue(
+ self.maxLineLength.value()
+ )
+ pythonSettingsTreeNode.childSetting("external-editor").setValue(
+ self.externalEditor.text()
+ )
def restoreSettings(self):
settings = QgsSettings()
- self.preloadAPI.setChecked(settings.value("pythonConsole/preloadAPI", True, type=bool))
- self.lineEdit.setText(settings.value("pythonConsole/preparedAPIFile", "", type=str))
+ self.preloadAPI.setChecked(
+ settings.value("pythonConsole/preloadAPI", True, type=bool)
+ )
+ self.lineEdit.setText(
+ settings.value("pythonConsole/preparedAPIFile", "", type=str)
+ )
itemTable = settings.value("pythonConsole/userAPI", [])
if itemTable:
self.tableWidget.setRowCount(0)
@@ -234,39 +287,67 @@ def restoreSettings(self):
apiName = pathSplit[-1][0:-4]
self.tableWidget.setItem(i, 0, QTableWidgetItem(apiName))
self.tableWidget.setItem(i, 1, QTableWidgetItem(itemTable[i]))
- self.autoSaveScript.setChecked(settings.value("pythonConsole/autoSaveScript", False, type=bool))
+ self.autoSaveScript.setChecked(
+ settings.value("pythonConsole/autoSaveScript", False, type=bool)
+ )
- self.autoCompThreshold.setValue(settings.value("pythonConsole/autoCompThreshold", 2, type=int))
- self.groupBoxAutoCompletion.setChecked(settings.value("pythonConsole/autoCompleteEnabled", True, type=bool))
+ self.autoCompThreshold.setValue(
+ settings.value("pythonConsole/autoCompThreshold", 2, type=int)
+ )
+ self.groupBoxAutoCompletion.setChecked(
+ settings.value("pythonConsole/autoCompleteEnabled", True, type=bool)
+ )
- self.enableObjectInspector.setChecked(settings.value("pythonConsole/enableObjectInsp", False, type=bool))
- self.autoCloseBracket.setChecked(settings.value("pythonConsole/autoCloseBracket", True, type=bool))
- self.autoSurround.setChecked(settings.value("pythonConsole/autoSurround", True, type=bool))
- self.autoInsertImport.setChecked(settings.value("pythonConsole/autoInsertImport", False, type=bool))
+ self.enableObjectInspector.setChecked(
+ settings.value("pythonConsole/enableObjectInsp", False, type=bool)
+ )
+ self.autoCloseBracket.setChecked(
+ settings.value("pythonConsole/autoCloseBracket", True, type=bool)
+ )
+ self.autoSurround.setChecked(
+ settings.value("pythonConsole/autoSurround", True, type=bool)
+ )
+ self.autoInsertImport.setChecked(
+ settings.value("pythonConsole/autoInsertImport", False, type=bool)
+ )
- pythonSettingsTreeNode = QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")
+ pythonSettingsTreeNode = (
+ QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")
+ )
- self.formatOnSave.setChecked(settings.value("pythonConsole/formatOnSave", False, type=bool))
- self.sortImports.setChecked(pythonSettingsTreeNode.childSetting("sort-imports").value())
- self.formatter.setCurrentText(pythonSettingsTreeNode.childSetting("formatter").value())
- self.autopep8Level.setValue(pythonSettingsTreeNode.childSetting("autopep8-level").value())
- self.blackNormalizeQuotes.setChecked(pythonSettingsTreeNode.childSetting("black-normalize-quotes").value())
- self.maxLineLength.setValue(pythonSettingsTreeNode.childSetting("max-line-length").value())
+ self.formatOnSave.setChecked(
+ settings.value("pythonConsole/formatOnSave", False, type=bool)
+ )
+ self.sortImports.setChecked(
+ pythonSettingsTreeNode.childSetting("sort-imports").value()
+ )
+ self.formatter.setCurrentText(
+ pythonSettingsTreeNode.childSetting("formatter").value()
+ )
+ self.autopep8Level.setValue(
+ pythonSettingsTreeNode.childSetting("autopep8-level").value()
+ )
+ self.blackNormalizeQuotes.setChecked(
+ pythonSettingsTreeNode.childSetting("black-normalize-quotes").value()
+ )
+ self.maxLineLength.setValue(
+ pythonSettingsTreeNode.childSetting("max-line-length").value()
+ )
- if settings.value("pythonConsole/autoCompleteSource") == 'fromDoc':
+ if settings.value("pythonConsole/autoCompleteSource") == "fromDoc":
self.autoCompFromDoc.setChecked(True)
- elif settings.value("pythonConsole/autoCompleteSource") == 'fromAPI':
+ elif settings.value("pythonConsole/autoCompleteSource") == "fromAPI":
self.autoCompFromAPI.setChecked(True)
- elif settings.value("pythonConsole/autoCompleteSource") == 'fromDocAPI':
+ elif settings.value("pythonConsole/autoCompleteSource") == "fromDocAPI":
self.autoCompFromDocAPI.setChecked(True)
self.externalEditor.setText(
- pythonSettingsTreeNode.childSetting('external-editor').value()
+ pythonSettingsTreeNode.childSetting("external-editor").value()
)
def onFormatterChanged(self):
- """ Toggle formatter-specific options visibility when the formatter is changed """
- if self.formatter.currentText() == 'autopep8':
+ """Toggle formatter-specific options visibility when the formatter is changed"""
+ if self.formatter.currentText() == "autopep8":
self.autopep8Level.setVisible(True)
self.autopep8LevelLabel.setVisible(True)
self.blackNormalizeQuotes.setVisible(False)
diff --git a/python/console/process_wrapper.py b/python/console/process_wrapper.py
index b59d8a6ead92..4d9acbd83606 100644
--- a/python/console/process_wrapper.py
+++ b/python/console/process_wrapper.py
@@ -15,7 +15,6 @@
***************************************************************************
"""
-
import locale
import os
import subprocess
@@ -43,7 +42,7 @@ def __init__(self, command, interactive=True, parent=None):
"stdout": subprocess.PIPE,
"stdin": subprocess.PIPE,
"stderr": subprocess.PIPE,
- "shell": True
+ "shell": True,
}
# On Unix, we can use os.setsid
@@ -64,12 +63,16 @@ def __init__(self, command, interactive=True, parent=None):
# Read process stdout and push to out queue
self.q_out = Queue()
- self.t_out = Thread(daemon=True, target=self.enqueue_output, args=[self.p.stdout, self.q_out])
+ self.t_out = Thread(
+ daemon=True, target=self.enqueue_output, args=[self.p.stdout, self.q_out]
+ )
self.t_out.start()
# Read process stderr and push to err queue
self.q_err = Queue()
- self.t_err = Thread(daemon=True, target=self.enqueue_output, args=[self.p.stderr, self.q_err])
+ self.t_err = Thread(
+ daemon=True, target=self.enqueue_output, args=[self.p.stderr, self.q_err]
+ )
self.t_err.start()
# Polls process and output both queues content to sys.stdout and sys.stderr
@@ -89,8 +92,10 @@ def enqueue_output(self, stream, queue):
stream.close()
def __repr__(self):
- """ Helpful representation of the maanaged process """
- status = "Running" if self.returncode is None else f"Completed ({self.returncode})"
+ """Helpful representation of the maanaged process"""
+ status = (
+ "Running" if self.returncode is None else f"Completed ({self.returncode})"
+ )
repr = f"ProcessWrapper object at {hex(id(self))}"
repr += f"\n - Status: {status}"
repr += f"\n - stdout: {self.stdout}"
@@ -111,7 +116,7 @@ def decode(self, bytes):
return text
def read_content(self, queue, stream, is_stderr):
- """ Write queue content to the standard stream and append it to the internal buffer """
+ """Write queue content to the standard stream and append it to the internal buffer"""
content = b""
while True:
try:
@@ -130,7 +135,7 @@ def read_content(self, queue, stream, is_stderr):
return
def dequeue_output(self):
- """ Check process every 0.1s and forward its outputs to stdout and stderr """
+ """Check process every 0.1s and forward its outputs to stdout and stderr"""
# Poll process and forward its outputs to stdout and stderr
while self.p.poll() is None:
@@ -151,11 +156,11 @@ def dequeue_output(self):
self.finished.emit(self.returncode)
def wait(self, timeout=1):
- """ Wait for the managed process to finish. If timeout=-1, waits indefinitely (and freeze the GUI) """
+ """Wait for the managed process to finish. If timeout=-1, waits indefinitely (and freeze the GUI)"""
self.p.wait(timeout)
def write(self, data):
- """ Send data to the managed process"""
+ """Send data to the managed process"""
try:
self.p.stdin.write((data + "\n").encode("utf8"))
self.p.stdin.flush()
@@ -165,7 +170,7 @@ def write(self, data):
self.finished.emit(self.p.poll())
def kill(self):
- """ Kill the managed process """
+ """Kill the managed process"""
# Process in run with shell=True, so calling self.p.kill() would only kill the shell
# (i.e a text editor launched with !gedit would not close) so we need to iterate
@@ -173,6 +178,7 @@ def kill(self):
try:
import psutil
+
if self.p.returncode is None:
process = psutil.Process(self.p.pid)
for child_process in process.children(recursive=True):
@@ -187,7 +193,7 @@ def kill(self):
self.p.kill()
def __del__(self):
- """ Ensure streams are closed when the process is destroyed """
+ """Ensure streams are closed when the process is destroyed"""
self.p.stdout.close()
self.p.stderr.close()
self.p.stdin.close()
diff --git a/python/core/additions/edit.py b/python/core/additions/edit.py
index 92756d1e8953..b82f268ea0a9 100644
--- a/python/core/additions/edit.py
+++ b/python/core/additions/edit.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
edit.py
@@ -17,8 +15,6 @@
***************************************************************************
"""
-from builtins import object
-
class QgsEditError(Exception):
@@ -29,7 +25,7 @@ def __str__(self):
return repr(self.value)
-class edit(object):
+class edit:
def __init__(self, layer):
self.layer = layer
diff --git a/python/core/additions/fromfunction.py b/python/core/additions/fromfunction.py
index 92c3647449b5..7bbe1761a975 100644
--- a/python/core/additions/fromfunction.py
+++ b/python/core/additions/fromfunction.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
fromfunction.py
@@ -23,36 +21,38 @@
@staticmethod
-def _fromFunction(description: str,
- function: _typing.Callable,
- *args,
- on_finished: _typing.Optional[_typing.Callable] = None,
- flags=QgsTask.Flag.AllFlags,
- **kwargs) -> QgsTask:
+def _fromFunction(
+ description: str,
+ function: _typing.Callable,
+ *args,
+ on_finished: _typing.Optional[_typing.Callable] = None,
+ flags=QgsTask.Flag.AllFlags,
+ **kwargs
+) -> QgsTask:
"""
-Creates a new QgsTask task from a python function.
+ Creates a new QgsTask task from a python function.
-Example
--------
+ Example
+ -------
-.. code-block:: python
+ .. code-block:: python
- def calculate(task):
- # pretend this is some complex maths and stuff we want
- # to run in the background
- return 5*6
+ def calculate(task):
+ # pretend this is some complex maths and stuff we want
+ # to run in the background
+ return 5*6
- def calculation_finished(exception, value=None):
- if not exception:
- iface.messageBar().pushMessage(
- 'the magic number is {}'.format(value))
- else:
- iface.messageBar().pushMessage(
- str(exception))
+ def calculation_finished(exception, value=None):
+ if not exception:
+ iface.messageBar().pushMessage(
+ 'the magic number is {}'.format(value))
+ else:
+ iface.messageBar().pushMessage(
+ str(exception))
- task = QgsTask.fromFunction('my task', calculate,
- on_finished=calculation_finished)
- QgsApplication.taskManager().addTask(task)
+ task = QgsTask.fromFunction('my task', calculate,
+ on_finished=calculation_finished)
+ QgsApplication.taskManager().addTask(task)
"""
diff --git a/python/core/additions/metaenum.py b/python/core/additions/metaenum.py
index dac7b1b1c464..0d1fb6b39683 100644
--- a/python/core/additions/metaenum.py
+++ b/python/core/additions/metaenum.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
metaenum.py
@@ -61,7 +59,9 @@ def metaEnumFromType(enumClass, baseClass=None, raiseException=True):
return metaEnumFromType(enumClass, baseClass, raiseException)
except AttributeError:
if raiseException:
- raise ValueError("Enum type does not implement baseClass method. Provide the base class as argument.")
+ raise ValueError(
+ "Enum type does not implement baseClass method. Provide the base class as argument."
+ )
try:
meta_object = baseClass.staticMetaObject
@@ -71,7 +71,7 @@ def metaEnumFromType(enumClass, baseClass=None, raiseException=True):
META_ENUM_BY_ENUM_CLASS[enumClass] = meta_enum
except AttributeError:
if raiseException:
- raise TypeError("could not get the metaEnum for {}".format(enumClass.__name__))
+ raise TypeError(f"could not get the metaEnum for {enumClass.__name__}")
meta_enum = None
return meta_enum
diff --git a/python/core/additions/projectdirtyblocker.py b/python/core/additions/projectdirtyblocker.py
index 7f06bd0a8412..95cec0432a79 100644
--- a/python/core/additions/projectdirtyblocker.py
+++ b/python/core/additions/projectdirtyblocker.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
projectdirtyblocker.py
@@ -17,11 +15,10 @@
***************************************************************************
"""
-
from qgis._core import QgsProjectDirtyBlocker
-class ProjectDirtyBlocker():
+class ProjectDirtyBlocker:
"""
Context manager used to block project setDirty calls.
diff --git a/python/core/additions/providermetadata.py b/python/core/additions/providermetadata.py
index d188f6e26fde..b1a676c37386 100644
--- a/python/core/additions/providermetadata.py
+++ b/python/core/additions/providermetadata.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
providermetadata.py
@@ -21,12 +19,12 @@
class PyProviderMetadata(QgsProviderMetadata):
- """ wrapper around QgsProviderMetadata to keep the existing Python code running which registers
- data providers by passing a custom python createProvider() function to QgsProviderMetadata
- constructor. The proper new way of doing it is to subclass QgsProviderMetadata and implement
- its virtual functions.
+ """wrapper around QgsProviderMetadata to keep the existing Python code running which registers
+ data providers by passing a custom python createProvider() function to QgsProviderMetadata
+ constructor. The proper new way of doing it is to subclass QgsProviderMetadata and implement
+ its virtual functions.
- TODO: QGIS 4 - remove this wrapper (only subclassing of QgsProviderMetadata should be used)
+ TODO: QGIS 4 - remove this wrapper (only subclassing of QgsProviderMetadata should be used)
"""
# this is a workaround to keep references to metadata classes
diff --git a/python/core/additions/qgsfeature.py b/python/core/additions/qgsfeature.py
index 4de498f766f8..0bd2a5a23695 100644
--- a/python/core/additions/qgsfeature.py
+++ b/python/core/additions/qgsfeature.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgsfeature.py
@@ -16,12 +14,18 @@
* *
***************************************************************************
"""
+
from PyQt5.QtCore import QVariant
def _mapping_feature(feature):
geom = feature.geometry()
- properties = {k: None if (v is None or (isinstance(v, QVariant) and v.isNull())) else v for k, v in feature.attributeMap().items()}
- return {'type': 'Feature',
- 'properties': properties,
- 'geometry': geom.__geo_interface__}
+ properties = {
+ k: None if (v is None or (isinstance(v, QVariant) and v.isNull())) else v
+ for k, v in feature.attributeMap().items()
+ }
+ return {
+ "type": "Feature",
+ "properties": properties,
+ "geometry": geom.__geo_interface__,
+ }
diff --git a/python/core/additions/qgsfunction.py b/python/core/additions/qgsfunction.py
index e91e25a7fc0b..32a338dac291 100644
--- a/python/core/additions/qgsfunction.py
+++ b/python/core/additions/qgsfunction.py
@@ -15,13 +15,18 @@
***************************************************************************
"""
-
import inspect
import string
import traceback
from qgis.PyQt.QtCore import QCoreApplication
-from qgis._core import QgsExpressionFunction, QgsExpression, QgsMessageLog, QgsFeatureRequest, Qgis
+from qgis._core import (
+ QgsExpressionFunction,
+ QgsExpression,
+ QgsMessageLog,
+ QgsFeatureRequest,
+ Qgis,
+)
class QgsPyExpressionFunction(QgsExpressionFunction):
@@ -143,7 +148,8 @@ def register_function(
if not QgsExpression.unregisterFunction(name):
msgtitle = QCoreApplication.translate("UserExpressions", "User expressions")
msg = QCoreApplication.translate(
- "UserExpressions", "The user expression {0} already exists and could not be unregistered."
+ "UserExpressions",
+ "The user expression {0} already exists and could not be unregistered.",
).format(name)
QgsMessageLog.logMessage(msg + "\n", msgtitle, Qgis.MessageLevel.Warning)
return None
@@ -154,7 +160,14 @@ def register_function(
# Legacy: if args was not 'auto', parameters were passed as a list
params_as_list = params_as_list or args != "auto"
f = QgsPyExpressionFunction(
- function, name, group, helptext, usesgeometry, referenced_columns, handlesnull, params_as_list
+ function,
+ name,
+ group,
+ helptext,
+ usesgeometry,
+ referenced_columns,
+ handlesnull,
+ params_as_list,
)
if register:
diff --git a/python/core/additions/qgsgeometry.py b/python/core/additions/qgsgeometry.py
index 6d2babfe1f03..63dbdeb5ccd7 100644
--- a/python/core/additions/qgsgeometry.py
+++ b/python/core/additions/qgsgeometry.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgsgeometry.py
diff --git a/python/core/additions/qgssettings.py b/python/core/additions/qgssettings.py
index a8190a31c7c9..8140211df44c 100644
--- a/python/core/additions/qgssettings.py
+++ b/python/core/additions/qgssettings.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettings.py
@@ -22,7 +20,9 @@
import qgis # required to get base class of enums
-def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_enum_value(
+ self, key, enumDefaultValue, section=QgsSettings.Section.NoSection
+):
"""
Return the setting value for a setting based on an enum.
This forces the output to be a valid and existing entry of the enum.
@@ -41,8 +41,11 @@ def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Sec
meta_enum = metaEnumFromValue(enumDefaultValue)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})"
- .format(enumDefaultValue.__class__))
+ raise ValueError(
+ "could not get the meta enum for given enum default value (type: {})".format(
+ enumDefaultValue.__class__
+ )
+ )
str_val = self.value(key, meta_enum.valueToKey(enumDefaultValue), str, section)
# need a new meta enum as QgsSettings.value is making a copy and leads to seg fault (probably a PyQt issue)
@@ -58,7 +61,9 @@ def _qgssettings_enum_value(self, key, enumDefaultValue, section=QgsSettings.Sec
return enu_val
-def _qgssettings_set_enum_value(self, key, enumValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_set_enum_value(
+ self, key, enumValue, section=QgsSettings.Section.NoSection
+):
"""
Save the setting value for a setting based on an enum.
This forces the output to be a valid and existing entry of the enum.
@@ -76,12 +81,16 @@ def _qgssettings_set_enum_value(self, key, enumValue, section=QgsSettings.Sectio
meta_enum = metaEnumFromValue(enumValue)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})".format(type(enumValue)))
+ raise ValueError(
+ f"could not get the meta enum for given enum default value (type: {type(enumValue)})"
+ )
self.setValue(key, meta_enum.valueToKey(enumValue), section)
-def _qgssettings_flag_value(self, key, flagDefaultValue, section=QgsSettings.Section.NoSection):
+def _qgssettings_flag_value(
+ self, key, flagDefaultValue, section=QgsSettings.Section.NoSection
+):
"""
Return the setting value for a setting based on a flag.
This forces the output to be a valid and existing entry of the enum.
@@ -102,13 +111,19 @@ def _qgssettings_flag_value(self, key, flagDefaultValue, section=QgsSettings.Sec
# dirty hack to get the parent class
__import__(flagDefaultValue.__module__)
baseClass = None
- exec("baseClass={module}.{flag_class}".format(module=flagDefaultValue.__module__.replace('_', ''),
- flag_class=flagDefaultValue.__class__.__name__))
+ exec(
+ "baseClass={module}.{flag_class}".format(
+ module=flagDefaultValue.__module__.replace("_", ""),
+ flag_class=flagDefaultValue.__class__.__name__,
+ )
+ )
meta_enum = metaEnumFromValue(flagDefaultValue, baseClass)
if meta_enum is None or not meta_enum.isValid():
# this should not happen
- raise ValueError("could not get the meta enum for given enum default value (type: {})".format(type(flagDefaultValue)))
+ raise ValueError(
+ f"could not get the meta enum for given enum default value (type: {type(flagDefaultValue)})"
+ )
str_val = self.value(key, meta_enum.valueToKeys(flagDefaultValue), str, section)
# need a new meta enum as QgsSettings.value is making a copy and leads to seg fault (probably a PyQt issue)
diff --git a/python/core/additions/qgssettingsentry.py b/python/core/additions/qgssettingsentry.py
index ca55c90e4f66..d07797c0d55c 100644
--- a/python/core/additions/qgssettingsentry.py
+++ b/python/core/additions/qgssettingsentry.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettingsentry.py
@@ -18,7 +16,13 @@
"""
from .metaenum import metaEnumFromValue
-from qgis.core import QgsSettings, QgsSettingsTree, QgsSettingsEntryBase, QgsLogger, Qgis
+from qgis.core import (
+ QgsSettings,
+ QgsSettingsTree,
+ QgsSettingsEntryBase,
+ QgsLogger,
+ Qgis,
+)
import qgis # required to get base class of enums
@@ -29,7 +33,14 @@ class PyQgsSettingsEntryEnumFlag
since QGIS 3.20
"""
- def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgis.SettingsOptions()):
+ def __init__(
+ self,
+ key,
+ pluginName,
+ defaultValue,
+ description="",
+ options=Qgis.SettingsOptions(),
+ ):
"""
Constructor for PyQgsSettingsEntryEnumFlag.
@@ -42,10 +53,12 @@ def __init__(self, key, pluginName, defaultValue, description=str(), options=Qgi
# TODO QGIS 4: rename pluginName arg to parent and key to name
self.options = options
- defaultValueStr = str()
+ defaultValueStr = ""
self.__metaEnum = metaEnumFromValue(defaultValue)
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
else:
if self.__metaEnum.isFlag():
defaultValueStr = self.__metaEnum.valueToKeys(defaultValue)
@@ -75,9 +88,13 @@ def value(self, dynamicKeyPart=None):
:param dynamicKeyPart: argument specifies the dynamic part of the settings key.
"""
if self.__metaEnum.isFlag():
- return QgsSettings().flagValue(self.key(dynamicKeyPart), self.defaultValue())
+ return QgsSettings().flagValue(
+ self.key(dynamicKeyPart), self.defaultValue()
+ )
else:
- return QgsSettings().enumValue(self.key(dynamicKeyPart), self.defaultValue())
+ return QgsSettings().enumValue(
+ self.key(dynamicKeyPart), self.defaultValue()
+ )
def valueWithDefaultOverride(self, defaultValueOverride, dynamicKeyPart=None):
"""
@@ -87,9 +104,13 @@ def valueWithDefaultOverride(self, defaultValueOverride, dynamicKeyPart=None):
:param dynamicKeyPart: argument specifies the dynamic part of the settings key.
"""
if self.__metaEnum.isFlag():
- return QgsSettings().flagValue(self.key(dynamicKeyPart), defaultValueOverride)
+ return QgsSettings().flagValue(
+ self.key(dynamicKeyPart), defaultValueOverride
+ )
else:
- return QgsSettings().enumValue(self.key(dynamicKeyPart), defaultValueOverride)
+ return QgsSettings().enumValue(
+ self.key(dynamicKeyPart), defaultValueOverride
+ )
def defaultValue(self):
"""
@@ -97,7 +118,9 @@ def defaultValue(self):
"""
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
return -1
defaultValueString = self.defaultValueAsVariant()
@@ -106,7 +129,9 @@ def defaultValue(self):
else:
(defaultValue, ok) = self.__metaEnum.keyToValue(defaultValueString)
if not ok:
- QgsLogger.debug("Invalid enum/flag key/s '{0}'.".format(self.defaultValueAsVariant()))
+ QgsLogger.debug(
+ f"Invalid enum/flag key/s '{self.defaultValueAsVariant()}'."
+ )
return -1
# cast to the enum class
@@ -122,7 +147,9 @@ def setValue(self, value, dynamicKeyPart: (list, str) = None):
"""
if self.__metaEnum is None or not self.__metaEnum.isValid():
- QgsLogger.debug("Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{0}'".format(self.key()))
+ QgsLogger.debug(
+ f"Invalid metaenum. Enum/Flag probably misses Q_ENUM/Q_FLAG declaration. Settings key: '{self.key()}'"
+ )
return False
if self.options & Qgis.SettingsOption.SaveEnumFlagAsInt:
@@ -133,7 +160,7 @@ def setValue(self, value, dynamicKeyPart: (list, str) = None):
else:
enum_flag_key = self.__metaEnum.valueToKey(value)
if not enum_flag_key:
- QgsLogger.debug("Invalid enum/flag value '{0}'.".format(value))
+ QgsLogger.debug(f"Invalid enum/flag value '{value}'.")
return False
if type(dynamicKeyPart) is str:
diff --git a/python/core/additions/qgstaskwrapper.py b/python/core/additions/qgstaskwrapper.py
index 6c2b7560d812..a3a7d8c347bf 100644
--- a/python/core/additions/qgstaskwrapper.py
+++ b/python/core/additions/qgstaskwrapper.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgstaskwrapper.py
@@ -17,7 +15,6 @@
***************************************************************************
"""
-
from qgis._core import QgsTask
@@ -47,7 +44,7 @@ def finished(self, result):
return
if not result and self.exception is None:
- self.exception = Exception('Task canceled')
+ self.exception = Exception("Task canceled")
try:
if self.returned_values:
diff --git a/python/core/additions/readwritecontextentercategory.py b/python/core/additions/readwritecontextentercategory.py
index af9c509342cd..79a9c44e8bac 100644
--- a/python/core/additions/readwritecontextentercategory.py
+++ b/python/core/additions/readwritecontextentercategory.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
readwritecontextentercategory.py
@@ -18,7 +16,7 @@
"""
-class ReadWriteContextEnterCategory():
+class ReadWriteContextEnterCategory:
"""
Push a category to the stack
diff --git a/python/core/additions/runtimeprofiler.py b/python/core/additions/runtimeprofiler.py
index 2216f5609e9d..95758a1ce899 100644
--- a/python/core/additions/runtimeprofiler.py
+++ b/python/core/additions/runtimeprofiler.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
runtimeprofiler.py
@@ -17,11 +15,10 @@
***************************************************************************
"""
-
from qgis._core import QgsScopedRuntimeProfile
-class ScopedRuntimeProfileContextManager():
+class ScopedRuntimeProfileContextManager:
"""
Context manager used to profile blocks of code in the QgsApplication.profiler() registry.
diff --git a/python/core/additions/validitycheck.py b/python/core/additions/validitycheck.py
index 25fdd1586f09..37a32b9cf5e1 100644
--- a/python/core/additions/validitycheck.py
+++ b/python/core/additions/validitycheck.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
validitycheck.py
@@ -16,9 +14,8 @@
* *
***************************************************************************
"""
-from qgis._core import (
- QgsAbstractValidityCheck,
- QgsApplication)
+
+from qgis._core import QgsAbstractValidityCheck, QgsApplication
class CheckFactory:
diff --git a/python/core/contextmanagers.py b/python/core/contextmanagers.py
index 62e0c624659d..72be360097e6 100644
--- a/python/core/contextmanagers.py
+++ b/python/core/contextmanagers.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
contextmanagers.py
@@ -17,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Nathan Woodrow'
-__date__ = 'May 2014'
-__copyright__ = '(C) 2014, Nathan Woodrow'
+__author__ = "Nathan Woodrow"
+__date__ = "May 2014"
+__copyright__ = "(C) 2014, Nathan Woodrow"
import sys
from contextlib import contextmanager
diff --git a/python/custom_widgets/qgis_customwidgets.py b/python/custom_widgets/qgis_customwidgets.py
index ade5d47bec89..90ca5579a479 100644
--- a/python/custom_widgets/qgis_customwidgets.py
+++ b/python/custom_widgets/qgis_customwidgets.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
customwidgets.py
@@ -50,8 +48,9 @@
def moduleInformation():
try:
import qgis.gui
+
widget_list = dir(qgis.gui)
- widget_list.remove('QgsScrollArea')
+ widget_list.remove("QgsScrollArea")
return "qgis.gui", widget_list
except ImportError:
return "", []
diff --git a/python/gui/additions/qgssettingsenumflageditorwrapper.py b/python/gui/additions/qgssettingsenumflageditorwrapper.py
index 245b0470a35f..528adb8192a2 100644
--- a/python/gui/additions/qgssettingsenumflageditorwrapper.py
+++ b/python/gui/additions/qgssettingsenumflageditorwrapper.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
qgssettingsenumflageditorwrapper.py
@@ -28,7 +26,9 @@ class PyQgsSettingsEnumEditorWidgetWrapper(QgsSettingsEditorWidgetWrapper):
A settings editor widget wrapper for enum settings as PyQgsSettingsEntryEnumFlag
"""
- def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict = None):
+ def __init__(
+ self, parent=None, editor=None, setting=None, displayStrings: dict = None
+ ):
self.setting = setting
self.editor = editor
self.displayStrings = {}
@@ -39,19 +39,23 @@ def __init__(self, parent=None, editor=None, setting=None, displayStrings: dict
self.configureEditor(editor, setting)
def id(self):
- return 'py-enum'
+ return "py-enum"
def createWrapper(self, parent=None):
return PyQgsSettingsEnumEditorWidgetWrapper(parent)
def setWidgetFromSetting(self):
if self.setting:
- return self.setWidgetFromVariant(self.setting.valueAsVariant(self.dynamicKeyPartList()))
+ return self.setWidgetFromVariant(
+ self.setting.valueAsVariant(self.dynamicKeyPartList())
+ )
return False
def setSettingFromWidget(self):
if self.editor:
- self.setting.setVariantValue(self.variantValueFromWidget(), self.dynamicKeyPartList())
+ self.setting.setVariantValue(
+ self.variantValueFromWidget(), self.dynamicKeyPartList()
+ )
return True
else:
return False
@@ -88,4 +92,7 @@ def configureEditorPrivate(self, editor: QComboBox, setting: QgsSettingsEntryBas
def enableAutomaticUpdatePrivate(self):
self.editor.currentIndexChanged.connect(
- lambda: self.setting.setValue(self.editor.currentData(), self.dynamicKeyPartList()))
+ lambda: self.setting.setValue(
+ self.editor.currentData(), self.dynamicKeyPartList()
+ )
+ )
diff --git a/python/plugins/MetaSearch/__init__.py b/python/plugins/MetaSearch/__init__.py
index ae8a29158156..069a287012c2 100644
--- a/python/plugins/MetaSearch/__init__.py
+++ b/python/plugins/MetaSearch/__init__.py
@@ -26,4 +26,5 @@
def classFactory(iface):
"""invoke plugin"""
from MetaSearch.plugin import MetaSearchPlugin
+
return MetaSearchPlugin(iface)
diff --git a/python/plugins/MetaSearch/dialogs/apidialog.py b/python/plugins/MetaSearch/dialogs/apidialog.py
index 03edfe8af3ff..a774f8af899a 100644
--- a/python/plugins/MetaSearch/dialogs/apidialog.py
+++ b/python/plugins/MetaSearch/dialogs/apidialog.py
@@ -24,17 +24,11 @@
import json
-from qgis.PyQt.QtWidgets import (
- QDialog,
- QVBoxLayout
-)
-from qgis.gui import (
- QgsCodeEditorJson,
- QgsCodeEditorHTML
-)
+from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout
+from qgis.gui import QgsCodeEditorJson, QgsCodeEditorHTML
from MetaSearch.util import get_ui_class, prettify_xml
-BASE_CLASS = get_ui_class('apidialog.ui')
+BASE_CLASS = get_ui_class("apidialog.ui")
class APIRequestResponseDialog(QDialog, BASE_CLASS):
@@ -46,7 +40,7 @@ def __init__(self, request, response, mime_type: str):
self.setupUi(self)
- if mime_type == 'json':
+ if mime_type == "json":
self.txtbrAPIRequest = QgsCodeEditorJson()
self.txtbrAPIResponse = QgsCodeEditorJson()
self.txtbrAPIRequest.setText(json.dumps(request, indent=4))
diff --git a/python/plugins/MetaSearch/dialogs/maindialog.py b/python/plugins/MetaSearch/dialogs/maindialog.py
index 96419750c4f3..36ece1f5c57b 100644
--- a/python/plugins/MetaSearch/dialogs/maindialog.py
+++ b/python/plugins/MetaSearch/dialogs/maindialog.py
@@ -31,15 +31,29 @@
from urllib.request import build_opener, install_opener, ProxyHandler
from qgis.PyQt.QtCore import Qt
-from qgis.PyQt.QtWidgets import (QDialog, QComboBox,
- QDialogButtonBox, QMessageBox,
- QTreeWidgetItem, QWidget)
+from qgis.PyQt.QtWidgets import (
+ QDialog,
+ QComboBox,
+ QDialogButtonBox,
+ QMessageBox,
+ QTreeWidgetItem,
+ QWidget,
+)
from qgis.PyQt.QtGui import QColor
-from qgis.core import (Qgis, QgsApplication, QgsCoordinateReferenceSystem,
- QgsCoordinateTransform, QgsGeometry, QgsPointXY,
- QgsProviderRegistry, QgsSettings, QgsProject,
- QgsRectangle, QgsSettingsTree)
+from qgis.core import (
+ Qgis,
+ QgsApplication,
+ QgsCoordinateReferenceSystem,
+ QgsCoordinateTransform,
+ QgsGeometry,
+ QgsPointXY,
+ QgsProviderRegistry,
+ QgsSettings,
+ QgsProject,
+ QgsRectangle,
+ QgsSettingsTree,
+)
from qgis.gui import QgsRubberBand, QgsGui
from qgis.utils import OverrideCursor
@@ -54,12 +68,19 @@
from MetaSearch.dialogs.recorddialog import RecordDialog
from MetaSearch.dialogs.apidialog import APIRequestResponseDialog
from MetaSearch.search_backend import get_catalog_service
-from MetaSearch.util import (clean_ows_url, get_connections_from_file,
- get_ui_class, get_help_url,
- normalize_text, open_url, render_template,
- serialize_string, StaticContext)
+from MetaSearch.util import (
+ clean_ows_url,
+ get_connections_from_file,
+ get_ui_class,
+ get_help_url,
+ normalize_text,
+ open_url,
+ render_template,
+ serialize_string,
+ StaticContext,
+)
-BASE_CLASS = get_ui_class('maindialog.ui')
+BASE_CLASS = get_ui_class("maindialog.ui")
class MetaSearchDialog(QDialog, BASE_CLASS):
@@ -82,9 +103,9 @@ def __init__(self, iface):
self.context = StaticContext()
self.leKeywords.setShowSearchIcon(True)
- self.leKeywords.setPlaceholderText(self.tr('Search keywords'))
+ self.leKeywords.setPlaceholderText(self.tr("Search keywords"))
- self.setWindowTitle(self.tr('MetaSearch'))
+ self.setWindowTitle(self.tr("MetaSearch"))
self.rubber_band = QgsRubberBand(self.map, Qgis.GeometryType.Polygon)
self.rubber_band.setColor(QColor(255, 0, 0, 75))
@@ -93,10 +114,11 @@ def __init__(self, iface):
# form inputs
self.startfrom = 1
self.constraints = []
- self.maxrecords = int(self.settings.value('/MetaSearch/returnRecords', 10))
- self.timeout = int(self.settings.value('/MetaSearch/timeout', 10))
+ self.maxrecords = int(self.settings.value("/MetaSearch/returnRecords", 10))
+ self.timeout = int(self.settings.value("/MetaSearch/timeout", 10))
self.disable_ssl_verification = self.settings.value(
- '/MetaSearch/disableSSL', False, bool)
+ "/MetaSearch/disableSSL", False, bool
+ )
# Services tab
self.cmbConnectionsServices.activated.connect(self.save_connection)
@@ -119,7 +141,9 @@ def __init__(self, iface):
self.btnSearch.clicked.connect(self.search)
self.leKeywords.returnPressed.connect(self.search)
# prevent dialog from closing upon pressing enter
- self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setAutoDefault(False)
+ self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setAutoDefault(
+ False
+ )
# launch help from button
self.buttonBox.helpRequested.connect(self.help)
self.btnCanvasBbox.setAutoDefault(False)
@@ -144,16 +168,17 @@ def __init__(self, iface):
def manageGui(self):
"""open window"""
+
def _on_timeout_change(value):
- self.settings.setValue('/MetaSearch/timeout', value)
+ self.settings.setValue("/MetaSearch/timeout", value)
self.timeout = value
def _on_records_change(value):
- self.settings.setValue('/MetaSearch/returnRecords', value)
+ self.settings.setValue("/MetaSearch/returnRecords", value)
self.maxrecords = value
def _on_ssl_state_change(state):
- self.settings.setValue('/MetaSearch/disableSSL', bool(state))
+ self.settings.setValue("/MetaSearch/disableSSL", bool(state))
self.disable_ssl_verification = bool(state)
self.tabWidget.setCurrentIndex(0)
@@ -168,11 +193,11 @@ def _on_ssl_state_change(state):
self.disableSSLVerification.setChecked(self.disable_ssl_verification)
self.disableSSLVerification.stateChanged.connect(_on_ssl_state_change)
- key = '/MetaSearch/%s' % self.cmbConnectionsSearch.currentText()
- self.catalog_url = self.settings.value('%s/url' % key)
- self.catalog_username = self.settings.value('%s/username' % key)
- self.catalog_password = self.settings.value('%s/password' % key)
- self.catalog_type = self.settings.value('%s/catalog-type' % key)
+ key = "/MetaSearch/%s" % self.cmbConnectionsSearch.currentText()
+ self.catalog_url = self.settings.value("%s/url" % key)
+ self.catalog_username = self.settings.value("%s/username" % key)
+ self.catalog_password = self.settings.value("%s/password" % key)
+ self.catalog_type = self.settings.value("%s/catalog-type" % key)
self.set_bbox_global()
@@ -186,7 +211,7 @@ def _on_ssl_state_change(state):
def populate_connection_list(self):
"""populate select box with connections"""
- self.settings.beginGroup('/MetaSearch/')
+ self.settings.beginGroup("/MetaSearch/")
self.cmbConnectionsServices.clear()
self.cmbConnectionsServices.addItems(self.settings.childGroups())
self.cmbConnectionsSearch.clear()
@@ -202,11 +227,13 @@ def populate_connection_list(self):
# and start with connection tab open
self.tabWidget.setCurrentIndex(1)
# tell the user to add services
- msg = self.tr('No services/connections defined. To get '
- 'started with MetaSearch, create a new '
- 'connection by clicking \'New\' or click '
- '\'Add default services\'.')
- self.textMetadata.setHtml('
%s
' % msg)
+ msg = self.tr(
+ "No services/connections defined. To get "
+ "started with MetaSearch, create a new "
+ "connection by clicking 'New' or click "
+ "'Add default services'."
+ )
+ self.textMetadata.setHtml("
%s
" % msg)
else:
# connections - enable various buttons
state_disabled = True
@@ -217,7 +244,7 @@ def populate_connection_list(self):
def set_connection_list_position(self):
"""set the current index to the selected connection"""
- to_select = self.settings.value('/MetaSearch/selected')
+ to_select = self.settings.value("/MetaSearch/selected")
conn_count = self.cmbConnectionsServices.count()
if conn_count == 0:
@@ -256,21 +283,21 @@ def save_connection(self):
caller = self.sender().objectName()
- if caller == 'cmbConnectionsServices': # servers tab
+ if caller == "cmbConnectionsServices": # servers tab
current_text = self.cmbConnectionsServices.currentText()
- elif caller == 'cmbConnectionsSearch': # search tab
+ elif caller == "cmbConnectionsSearch": # search tab
current_text = self.cmbConnectionsSearch.currentText()
- self.settings.setValue('/MetaSearch/selected', current_text)
- key = '/MetaSearch/%s' % current_text
+ self.settings.setValue("/MetaSearch/selected", current_text)
+ key = "/MetaSearch/%s" % current_text
- if caller == 'cmbConnectionsSearch': # bind to service in search tab
- self.catalog_url = self.settings.value('%s/url' % key)
- self.catalog_username = self.settings.value('%s/username' % key)
- self.catalog_password = self.settings.value('%s/password' % key)
- self.catalog_type = self.settings.value('%s/catalog-type' % key)
+ if caller == "cmbConnectionsSearch": # bind to service in search tab
+ self.catalog_url = self.settings.value("%s/url" % key)
+ self.catalog_username = self.settings.value("%s/username" % key)
+ self.catalog_password = self.settings.value("%s/password" % key)
+ self.catalog_type = self.settings.value("%s/catalog-type" % key)
- if caller == 'cmbConnectionsServices': # clear server metadata
+ if caller == "cmbConnectionsServices": # clear server metadata
self.textMetadata.clear()
self.btnRawAPIResponse.setEnabled(False)
@@ -279,11 +306,11 @@ def connection_info(self):
"""show connection info"""
current_text = self.cmbConnectionsServices.currentText()
- key = '/MetaSearch/%s' % current_text
- self.catalog_url = self.settings.value('%s/url' % key)
- self.catalog_username = self.settings.value('%s/username' % key)
- self.catalog_password = self.settings.value('%s/password' % key)
- self.catalog_type = self.settings.value('%s/catalog-type' % key)
+ key = "/MetaSearch/%s" % current_text
+ self.catalog_url = self.settings.value("%s/url" % key)
+ self.catalog_username = self.settings.value("%s/username" % key)
+ self.catalog_password = self.settings.value("%s/password" % key)
+ self.catalog_type = self.settings.value("%s/catalog-type" % key)
# connect to the server
if not self._get_catalog():
@@ -291,9 +318,12 @@ def connection_info(self):
if self.catalog: # display service metadata
self.btnRawAPIResponse.setEnabled(True)
- metadata = render_template('en', self.context,
- self.catalog.conn,
- self.catalog.service_info_template)
+ metadata = render_template(
+ "en",
+ self.context,
+ self.catalog.conn,
+ self.catalog.service_info_template,
+ )
style = QgsApplication.reportStyleSheet()
self.textMetadata.clear()
self.textMetadata.document().setDefaultStyleSheet(style)
@@ -306,7 +336,7 @@ def add_connection(self):
"""add new service"""
conn_new = NewConnectionDialog()
- conn_new.setWindowTitle(self.tr('New Catalog Service'))
+ conn_new.setWindowTitle(self.tr("New Catalog Service"))
if conn_new.exec() == QDialog.DialogCode.Accepted: # add to service list
self.populate_connection_list()
self.textMetadata.clear()
@@ -316,19 +346,22 @@ def edit_connection(self):
current_text = self.cmbConnectionsServices.currentText()
- url = self.settings.value('/MetaSearch/%s/url' % current_text)
+ url = self.settings.value("/MetaSearch/%s/url" % current_text)
conn_edit = NewConnectionDialog(current_text)
- conn_edit.setWindowTitle(self.tr('Edit Catalog Service'))
+ conn_edit.setWindowTitle(self.tr("Edit Catalog Service"))
conn_edit.leName.setText(current_text)
conn_edit.leURL.setText(url)
conn_edit.leUsername.setText(
- self.settings.value('/MetaSearch/%s/username' % current_text))
+ self.settings.value("/MetaSearch/%s/username" % current_text)
+ )
conn_edit.lePassword.setText(
- self.settings.value('/MetaSearch/%s/password' % current_text))
+ self.settings.value("/MetaSearch/%s/password" % current_text)
+ )
conn_edit.cmbCatalogType.setCurrentText(
- self.settings.value('/MetaSearch/%s/catalog-type' % current_text))
+ self.settings.value("/MetaSearch/%s/catalog-type" % current_text)
+ )
if conn_edit.exec() == QDialog.DialogCode.Accepted: # update service list
self.populate_connection_list()
@@ -338,13 +371,17 @@ def delete_connection(self):
current_text = self.cmbConnectionsServices.currentText()
- key = '/MetaSearch/%s' % current_text
+ key = "/MetaSearch/%s" % current_text
- msg = self.tr('Remove service {0}?').format(current_text)
+ msg = self.tr("Remove service {0}?").format(current_text)
result = QMessageBox.question(
- self, self.tr('Delete Service'), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No)
+ self,
+ self.tr("Delete Service"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ QMessageBox.StandardButton.No,
+ )
if result == QMessageBox.StandardButton.Yes: # remove service from list
self.settings.remove(key)
index_to_delete = self.cmbConnectionsServices.currentIndex()
@@ -361,32 +398,39 @@ def load_connections(self):
def add_default_connections(self):
"""add default connections"""
- filename = os.path.join(self.context.ppath,
- 'resources', 'connections-default.xml')
+ filename = os.path.join(
+ self.context.ppath, "resources", "connections-default.xml"
+ )
doc = get_connections_from_file(self, filename)
if doc is None:
return
- self.settings.beginGroup('/MetaSearch/')
+ self.settings.beginGroup("/MetaSearch/")
keys = self.settings.childGroups()
self.settings.endGroup()
- for server in doc.findall('csw'):
- name = server.attrib.get('name')
+ for server in doc.findall("csw"):
+ name = server.attrib.get("name")
# check for duplicates
if name in keys:
- msg = self.tr('{0} exists. Overwrite?').format(name)
- res = QMessageBox.warning(self,
- self.tr('Loading connections'), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ msg = self.tr("{0} exists. Overwrite?").format(name)
+ res = QMessageBox.warning(
+ self,
+ self.tr("Loading connections"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
continue
# no dups detected or overwrite is allowed
- key = '/MetaSearch/%s' % name
- self.settings.setValue('%s/url' % key, server.attrib.get('url'))
- self.settings.setValue('%s/catalog-type' % key, server.attrib.get('catalog-type', 'OGC CSW 2.0.2'))
+ key = "/MetaSearch/%s" % name
+ self.settings.setValue("%s/url" % key, server.attrib.get("url"))
+ self.settings.setValue(
+ "%s/catalog-type" % key,
+ server.attrib.get("catalog-type", "OGC CSW 2.0.2"),
+ )
self.populate_connection_list()
@@ -395,17 +439,17 @@ def add_default_connections(self):
def set_ows_save_title_ask(self):
"""save ows save strategy as save ows title, ask if duplicate"""
- self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_ask')
+ self.settings.setValue("/MetaSearch/ows_save_strategy", "title_ask")
def set_ows_save_title_no_ask(self):
"""save ows save strategy as save ows title, do NOT ask if duplicate"""
- self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_no_ask')
+ self.settings.setValue("/MetaSearch/ows_save_strategy", "title_no_ask")
def set_ows_save_temp_name(self):
"""save ows save strategy as save with a temporary name"""
- self.settings.setValue('/MetaSearch/ows_save_strategy', 'temp_name')
+ self.settings.setValue("/MetaSearch/ows_save_strategy", "temp_name")
# Search tab
@@ -417,14 +461,12 @@ def set_bbox_from_map(self):
extent = self.map.extent()
- if crsid != 'EPSG:4326': # reproject to EPSG:4326
+ if crsid != "EPSG:4326": # reproject to EPSG:4326
src = QgsCoordinateReferenceSystem(crsid)
dest = QgsCoordinateReferenceSystem("EPSG:4326")
xform = QgsCoordinateTransform(src, dest, QgsProject.instance())
- minxy = xform.transform(QgsPointXY(extent.xMinimum(),
- extent.yMinimum()))
- maxxy = xform.transform(QgsPointXY(extent.xMaximum(),
- extent.yMaximum()))
+ minxy = xform.transform(QgsPointXY(extent.xMinimum(), extent.yMinimum()))
+ maxxy = xform.transform(QgsPointXY(extent.xMaximum(), extent.yMaximum()))
minx, miny = minxy
maxx, maxy = maxxy
else: # EPSG:4326
@@ -440,10 +482,10 @@ def set_bbox_from_map(self):
def set_bbox_global(self):
"""set global bounding box"""
- self.leNorth.setText('90')
- self.leSouth.setText('-90')
- self.leWest.setText('-180')
- self.leEast.setText('180')
+ self.leNorth.setText("90")
+ self.leSouth.setText("-90")
+ self.leWest.setText("-180")
+ self.leEast.setText("180")
def search(self):
"""execute search"""
@@ -456,11 +498,11 @@ def search(self):
# set current catalog
current_text = self.cmbConnectionsSearch.currentText()
- key = '/MetaSearch/%s' % current_text
- self.catalog_url = self.settings.value('%s/url' % key)
- self.catalog_username = self.settings.value('%s/username' % key)
- self.catalog_password = self.settings.value('%s/password' % key)
- self.catalog_type = self.settings.value('%s/catalog-type' % key)
+ key = "/MetaSearch/%s" % current_text
+ self.catalog_url = self.settings.value("%s/url" % key)
+ self.catalog_username = self.settings.value("%s/username" % key)
+ self.catalog_password = self.settings.value("%s/password" % key)
+ self.catalog_type = self.settings.value("%s/catalog-type" % key)
# start position and number of records to return
self.startfrom = 1
@@ -483,16 +525,18 @@ def search(self):
# to find ('service', 'dataset', etc.)
try:
with OverrideCursor(Qt.CursorShape.WaitCursor):
- self.catalog.query_records(bbox, keywords, self.maxrecords,
- self.startfrom)
+ self.catalog.query_records(
+ bbox, keywords, self.maxrecords, self.startfrom
+ )
except Exception as err:
- QMessageBox.warning(self, self.tr('Search error'),
- self.tr('Search error: {0}').format(err))
+ QMessageBox.warning(
+ self, self.tr("Search error"), self.tr("Search error: {0}").format(err)
+ )
return
if self.catalog.matches == 0:
- self.lblResults.setText(self.tr('0 results'))
+ self.lblResults.setText(self.tr("0 results"))
return
self.display_results()
@@ -504,21 +548,24 @@ def display_results(self):
position = self.catalog.returned + self.startfrom - 1
- msg = self.tr('Showing {0} - {1} of %n result(s)', 'number of results',
- self.catalog.matches).format(self.startfrom, position)
+ msg = self.tr(
+ "Showing {0} - {1} of %n result(s)",
+ "number of results",
+ self.catalog.matches,
+ ).format(self.startfrom, position)
self.lblResults.setText(msg)
for rec in self.catalog.records():
item = QTreeWidgetItem(self.treeRecords)
- if rec['type']:
- item.setText(0, normalize_text(rec['type']))
+ if rec["type"]:
+ item.setText(0, normalize_text(rec["type"]))
else:
- item.setText(0, 'unknown')
- if rec['title']:
- item.setText(1, normalize_text(rec['title']))
- if rec['identifier']:
- set_item_data(item, 'identifier', rec['identifier'])
+ item.setText(0, "unknown")
+ if rec["title"]:
+ item.setText(1, normalize_text(rec["title"]))
+ if rec["identifier"]:
+ set_item_data(item, "identifier", rec["identifier"])
self.btnViewRawAPIResponse.setEnabled(True)
@@ -555,36 +602,43 @@ def record_clicked(self):
if not item:
return
- identifier = get_item_data(item, 'identifier')
+ identifier = get_item_data(item, "identifier")
try:
- record = next(item for item in self.catalog.records()
- if item['identifier'] == identifier)
+ record = next(
+ item
+ for item in self.catalog.records()
+ if item["identifier"] == identifier
+ )
except KeyError:
- QMessageBox.warning(self,
- self.tr('Record parsing error'),
- 'Unable to locate record identifier')
+ QMessageBox.warning(
+ self,
+ self.tr("Record parsing error"),
+ "Unable to locate record identifier",
+ )
return
# if the record has a bbox, show a footprint on the map
- if record['bbox'] is not None:
- bx = record['bbox']
- rt = QgsRectangle(float(bx['minx']), float(bx['miny']),
- float(bx['maxx']), float(bx['maxy']))
+ if record["bbox"] is not None:
+ bx = record["bbox"]
+ rt = QgsRectangle(
+ float(bx["minx"]),
+ float(bx["miny"]),
+ float(bx["maxx"]),
+ float(bx["maxy"]),
+ )
geom = QgsGeometry.fromRect(rt)
if geom is not None:
src = QgsCoordinateReferenceSystem("EPSG:4326")
dst = self.map.mapSettings().destinationCrs()
if src.postgisSrid() != dst.postgisSrid():
- ctr = QgsCoordinateTransform(
- src, dst, QgsProject.instance())
+ ctr = QgsCoordinateTransform(src, dst, QgsProject.instance())
try:
geom.transform(ctr)
except Exception as err:
QMessageBox.warning(
- self,
- self.tr('Coordinate Transformation Error'),
- str(err))
+ self, self.tr("Coordinate Transformation Error"), str(err)
+ )
self.rubber_band.setToGeometry(geom, None)
# figure out if the data is interactive and can be operated on
@@ -594,73 +648,78 @@ def find_services(self, record, item):
"""scan record for WMS/WMTS|WFS|WCS endpoints"""
services = {}
- for link in record['links']:
+ for link in record["links"]:
link = self.catalog.parse_link(link)
- if 'scheme' in link:
- link_type = link['scheme']
- elif 'protocol' in link:
- link_type = link['protocol']
+ if "scheme" in link:
+ link_type = link["scheme"]
+ elif "protocol" in link:
+ link_type = link["protocol"]
else:
link_type = None
if link_type is not None:
link_type = link_type.upper()
- wmswmst_link_types = list(
- map(str.upper, link_types.WMSWMST_LINK_TYPES))
+ wmswmst_link_types = list(map(str.upper, link_types.WMSWMST_LINK_TYPES))
wfs_link_types = list(map(str.upper, link_types.WFS_LINK_TYPES))
wcs_link_types = list(map(str.upper, link_types.WCS_LINK_TYPES))
ams_link_types = list(map(str.upper, link_types.AMS_LINK_TYPES))
afs_link_types = list(map(str.upper, link_types.AFS_LINK_TYPES))
- gis_file_link_types = list(
- map(str.upper, link_types.GIS_FILE_LINK_TYPES))
+ gis_file_link_types = list(map(str.upper, link_types.GIS_FILE_LINK_TYPES))
# if the link type exists, and it is one of the acceptable
# interactive link types, then set
- all_link_types = (wmswmst_link_types + wfs_link_types +
- wcs_link_types + ams_link_types +
- afs_link_types + gis_file_link_types)
+ all_link_types = (
+ wmswmst_link_types
+ + wfs_link_types
+ + wcs_link_types
+ + ams_link_types
+ + afs_link_types
+ + gis_file_link_types
+ )
if all([link_type is not None, link_type in all_link_types]):
if link_type in wmswmst_link_types:
- services['wms'] = link['url']
+ services["wms"] = link["url"]
self.mActionAddWms.setEnabled(True)
if link_type in wfs_link_types:
- services['wfs'] = link['url']
+ services["wfs"] = link["url"]
self.mActionAddWfs.setEnabled(True)
if link_type in wcs_link_types:
- services['wcs'] = link['url']
+ services["wcs"] = link["url"]
self.mActionAddWcs.setEnabled(True)
if link_type in ams_link_types:
- services['ams'] = link['url']
+ services["ams"] = link["url"]
self.mActionAddAms.setEnabled(True)
if link_type in afs_link_types:
- services['afs'] = link['url']
+ services["afs"] = link["url"]
self.mActionAddAfs.setEnabled(True)
if link_type in gis_file_link_types:
- services['gis_file'] = link['url']
- services['title'] = record.get('title', '')
+ services["gis_file"] = link["url"]
+ services["title"] = record.get("title", "")
self.mActionAddGisFile.setEnabled(True)
self.tbAddData.setEnabled(True)
- set_item_data(item, 'link', json.dumps(services))
+ set_item_data(item, "link", json.dumps(services))
def navigate(self):
"""manage navigation / paging"""
caller = self.sender().objectName()
- if caller == 'btnFirst':
+ if caller == "btnFirst":
self.startfrom = 1
- elif caller == 'btnLast':
+ elif caller == "btnLast":
self.startfrom = self.catalog.matches - self.maxrecords + 1
- elif caller == 'btnNext':
+ elif caller == "btnNext":
if self.startfrom > self.catalog.matches - self.maxrecords:
- msg = self.tr('End of results. Go to start?')
- res = QMessageBox.information(self, self.tr('Navigation'),
- msg,
- (QMessageBox.StandardButton.Ok |
- QMessageBox.StandardButton.Cancel))
+ msg = self.tr("End of results. Go to start?")
+ res = QMessageBox.information(
+ self,
+ self.tr("Navigation"),
+ msg,
+ (QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel),
+ )
if res == QMessageBox.StandardButton.Ok:
self.startfrom = 1
else:
@@ -669,14 +728,15 @@ def navigate(self):
self.startfrom += self.maxrecords
elif caller == "btnPrev":
if self.startfrom == 1:
- msg = self.tr('Start of results. Go to end?')
- res = QMessageBox.information(self, self.tr('Navigation'),
- msg,
- (QMessageBox.StandardButton.Ok |
- QMessageBox.StandardButton.Cancel))
+ msg = self.tr("Start of results. Go to end?")
+ res = QMessageBox.information(
+ self,
+ self.tr("Navigation"),
+ msg,
+ (QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel),
+ )
if res == QMessageBox.StandardButton.Ok:
- self.startfrom = (self.catalog.matches -
- self.maxrecords + 1)
+ self.startfrom = self.catalog.matches - self.maxrecords + 1
else:
return
elif self.startfrom <= self.maxrecords:
@@ -696,12 +756,13 @@ def navigate(self):
try:
with OverrideCursor(Qt.CursorShape.WaitCursor):
- self.catalog.query_records(bbox, keywords,
- limit=self.maxrecords,
- offset=self.startfrom)
+ self.catalog.query_records(
+ bbox, keywords, limit=self.maxrecords, offset=self.startfrom
+ )
except Exception as err:
- QMessageBox.warning(self, self.tr('Search error'),
- self.tr('Search error: {0}').format(err))
+ QMessageBox.warning(
+ self, self.tr("Search error"), self.tr("Search error: {0}").format(err)
+ )
return
self.display_results()
@@ -716,42 +777,56 @@ def add_to_ows(self):
if not item:
return
- item_data = json.loads(get_item_data(item, 'link'))
+ item_data = json.loads(get_item_data(item, "link"))
caller = self.sender().objectName()
- if caller == 'mActionAddWms':
- service_type = 'OGC:WMS/OGC:WMTS'
- sname = 'WMS'
- dyn_param = ['wms']
- provider_name = 'wms'
- setting_node = QgsSettingsTree.node('connections').childNode('ows').childNode('connections')
- data_url = item_data['wms']
- elif caller == 'mActionAddWfs':
- service_type = 'OGC:WFS'
- sname = 'WFS'
- dyn_param = ['wfs']
- provider_name = 'WFS'
- setting_node = QgsSettingsTree.node('connections').childNode('ows').childNode('connections')
- data_url = item_data['wfs']
- elif caller == 'mActionAddWcs':
- service_type = 'OGC:WCS'
- sname = 'WCS'
- dyn_param = ['wcs']
- provider_name = 'wcs'
- setting_node = QgsSettingsTree.node('connections').childNode('ows').childNode('connections')
- data_url = item_data['wcs']
- elif caller == 'mActionAddAfs':
- service_type = 'ESRI:ArcGIS:FeatureServer'
- sname = 'AFS'
+ if caller == "mActionAddWms":
+ service_type = "OGC:WMS/OGC:WMTS"
+ sname = "WMS"
+ dyn_param = ["wms"]
+ provider_name = "wms"
+ setting_node = (
+ QgsSettingsTree.node("connections")
+ .childNode("ows")
+ .childNode("connections")
+ )
+ data_url = item_data["wms"]
+ elif caller == "mActionAddWfs":
+ service_type = "OGC:WFS"
+ sname = "WFS"
+ dyn_param = ["wfs"]
+ provider_name = "WFS"
+ setting_node = (
+ QgsSettingsTree.node("connections")
+ .childNode("ows")
+ .childNode("connections")
+ )
+ data_url = item_data["wfs"]
+ elif caller == "mActionAddWcs":
+ service_type = "OGC:WCS"
+ sname = "WCS"
+ dyn_param = ["wcs"]
+ provider_name = "wcs"
+ setting_node = (
+ QgsSettingsTree.node("connections")
+ .childNode("ows")
+ .childNode("connections")
+ )
+ data_url = item_data["wcs"]
+ elif caller == "mActionAddAfs":
+ service_type = "ESRI:ArcGIS:FeatureServer"
+ sname = "AFS"
dyn_param = []
- provider_name = 'arcgisfeatureserver'
- setting_node = QgsSettingsTree.node('connections').childNode('arcgisfeatureserver')
- data_url = (item_data['afs'].split('FeatureServer')[0] + 'FeatureServer')
+ provider_name = "arcgisfeatureserver"
+ setting_node = QgsSettingsTree.node("connections").childNode(
+ "arcgisfeatureserver"
+ )
+ data_url = item_data["afs"].split("FeatureServer")[0] + "FeatureServer"
keys = setting_node.items(dyn_param)
- sname = '%s from MetaSearch' % sname
+ sname = "%s from MetaSearch" % sname
for key in keys:
if key.startswith(sname):
conn_name_matches.append(key)
@@ -760,10 +835,15 @@ def add_to_ows(self):
# check for duplicates
if sname in keys: # duplicate found
- msg = self.tr('Connection {0} exists. Overwrite?').format(sname)
+ msg = self.tr("Connection {0} exists. Overwrite?").format(sname)
res = QMessageBox.warning(
- self, self.tr('Saving server'), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel)
+ self,
+ self.tr("Saving server"),
+ msg,
+ QMessageBox.StandardButton.Yes
+ | QMessageBox.StandardButton.No
+ | QMessageBox.StandardButton.Cancel,
+ )
if res == QMessageBox.StandardButton.No: # assign new name with serial
sname = serialize_string(sname)
elif res == QMessageBox.StandardButton.Cancel:
@@ -771,37 +851,41 @@ def add_to_ows(self):
# no dups detected or overwrite is allowed
dyn_param.append(sname)
- setting_node.childSetting('url').setValue(clean_ows_url(data_url), dyn_param)
+ setting_node.childSetting("url").setValue(clean_ows_url(data_url), dyn_param)
# open provider window
- ows_provider = QgsGui.sourceSelectProviderRegistry().\
- createSelectionWidget(
- provider_name, self, Qt.WindowType.Widget,
- QgsProviderRegistry.WidgetMode.Embedded)
+ ows_provider = QgsGui.sourceSelectProviderRegistry().createSelectionWidget(
+ provider_name,
+ self,
+ Qt.WindowType.Widget,
+ QgsProviderRegistry.WidgetMode.Embedded,
+ )
# connect dialog signals to iface slots
- if service_type == 'OGC:WMS/OGC:WMTS':
+ if service_type == "OGC:WMS/OGC:WMTS":
ows_provider.addRasterLayer.connect(self.iface.addRasterLayer)
- conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections')
- connect = 'btnConnect_clicked'
- elif service_type == 'OGC:WFS':
+ conn_cmb = ows_provider.findChild(QWidget, "cmbConnections")
+ connect = "btnConnect_clicked"
+ elif service_type == "OGC:WFS":
+
def addVectorLayer(path, name):
- self.iface.addVectorLayer(path, name, 'WFS')
+ self.iface.addVectorLayer(path, name, "WFS")
ows_provider.addVectorLayer.connect(addVectorLayer)
- conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections')
- connect = 'connectToServer'
- elif service_type == 'OGC:WCS':
+ conn_cmb = ows_provider.findChild(QWidget, "cmbConnections")
+ connect = "connectToServer"
+ elif service_type == "OGC:WCS":
ows_provider.addRasterLayer.connect(self.iface.addRasterLayer)
- conn_cmb = ows_provider.findChild(QWidget, 'mConnectionsComboBox')
- connect = 'mConnectButton_clicked'
- elif service_type == 'ESRI:ArcGIS:FeatureServer':
+ conn_cmb = ows_provider.findChild(QWidget, "mConnectionsComboBox")
+ connect = "mConnectButton_clicked"
+ elif service_type == "ESRI:ArcGIS:FeatureServer":
+
def addAfsLayer(path, name):
- self.iface.addVectorLayer(path, name, 'afs')
+ self.iface.addVectorLayer(path, name, "afs")
ows_provider.addVectorLayer.connect(addAfsLayer)
conn_cmb = ows_provider.findChild(QComboBox)
- connect = 'connectToServer'
+ connect = "connectToServer"
ows_provider.setModal(False)
ows_provider.show()
@@ -811,9 +895,9 @@ def addAfsLayer(path, name):
if index > -1:
conn_cmb.setCurrentIndex(index)
# only for wfs
- if service_type == 'OGC:WFS':
+ if service_type == "OGC:WFS":
ows_provider.cmbConnections_activated(index)
- elif service_type == 'ESRI:ArcGIS:FeatureServer':
+ elif service_type == "ESRI:ArcGIS:FeatureServer":
ows_provider.cmbConnections_activated(index)
getattr(ows_provider, connect)()
@@ -824,10 +908,10 @@ def add_gis_file(self):
if not item:
return
- item_data = json.loads(get_item_data(item, 'link'))
- gis_file = item_data['gis_file']
+ item_data = json.loads(get_item_data(item, "link"))
+ gis_file = item_data["gis_file"]
- title = item_data['title']
+ title = item_data["title"]
layer = self.iface.addVectorLayer(gis_file, title, "ogr")
if not layer:
@@ -843,7 +927,7 @@ def show_metadata(self):
if not item:
return
- identifier = get_item_data(item, 'identifier')
+ identifier = get_item_data(item, "identifier")
auth = None
@@ -855,32 +939,39 @@ def show_metadata(self):
try:
with OverrideCursor(Qt.CursorShape.WaitCursor):
- cat = get_catalog_service(self.catalog_url, # spellok
- catalog_type=self.catalog_type,
- timeout=self.timeout,
- username=self.catalog_username or None,
- password=self.catalog_password or None,
- auth=auth)
+ cat = get_catalog_service(
+ self.catalog_url, # spellok
+ catalog_type=self.catalog_type,
+ timeout=self.timeout,
+ username=self.catalog_username or None,
+ password=self.catalog_password or None,
+ auth=auth,
+ )
record = cat.get_record(identifier)
- if cat.type == 'OGC API - Records':
- record['url'] = cat.conn.request
- elif cat.type == 'OGC CSW 2.0.2':
+ if cat.type == "OGC API - Records":
+ record["url"] = cat.conn.request
+ elif cat.type == "OGC CSW 2.0.2":
record.url = cat.conn.request
except Exception as err:
QMessageBox.warning(
- self, self.tr('GetRecords error'),
- self.tr('Error getting response: {0}').format(err))
+ self,
+ self.tr("GetRecords error"),
+ self.tr("Error getting response: {0}").format(err),
+ )
return
except KeyError as err:
QMessageBox.warning(
- self, self.tr('Record parsing error'),
- self.tr('Unable to locate record identifier: {0}').format(err))
+ self,
+ self.tr("Record parsing error"),
+ self.tr("Unable to locate record identifier: {0}").format(err),
+ )
return
crd = RecordDialog()
- metadata = render_template('en', self.context,
- record, self.catalog.record_info_template)
+ metadata = render_template(
+ "en", self.context, record, self.catalog.record_info_template
+ )
style = QgsApplication.reportStyleSheet()
crd.textMetadata.document().setDefaultStyleSheet(style)
@@ -891,9 +982,7 @@ def show_api(self):
"""show API request / response"""
crd = APIRequestResponseDialog(
- self.catalog.request,
- self.catalog.response,
- self.catalog.format
+ self.catalog.request, self.catalog.response, self.catalog.format
)
crd.exec()
@@ -944,41 +1033,45 @@ def _get_catalog(self):
with OverrideCursor(Qt.CursorShape.WaitCursor):
try:
self.catalog = get_catalog_service(
- self.catalog_url, catalog_type=self.catalog_type,
- timeout=self.timeout, username=self.catalog_username or None,
- password=self.catalog_password or None, auth=auth)
+ self.catalog_url,
+ catalog_type=self.catalog_type,
+ timeout=self.timeout,
+ username=self.catalog_username or None,
+ password=self.catalog_password or None,
+ auth=auth,
+ )
return True
except Exception as err:
- msg = self.tr('Error connecting to service: {0}').format(err)
+ msg = self.tr("Error connecting to service: {0}").format(err)
- QMessageBox.warning(self, self.tr('CSW Connection error'), msg)
+ QMessageBox.warning(self, self.tr("CSW Connection error"), msg)
return False
def install_proxy(self):
"""set proxy if one is set in QGIS network settings"""
# initially support HTTP for now
- if self.settings.value('/proxy/proxyEnabled') == 'true':
- if self.settings.value('/proxy/proxyType') == 'HttpProxy':
- ptype = 'http'
+ if self.settings.value("/proxy/proxyEnabled") == "true":
+ if self.settings.value("/proxy/proxyType") == "HttpProxy":
+ ptype = "http"
else:
return
- user = self.settings.value('/proxy/proxyUser')
- password = self.settings.value('/proxy/proxyPassword')
- host = self.settings.value('/proxy/proxyHost')
- port = self.settings.value('/proxy/proxyPort')
+ user = self.settings.value("/proxy/proxyUser")
+ password = self.settings.value("/proxy/proxyPassword")
+ host = self.settings.value("/proxy/proxyHost")
+ port = self.settings.value("/proxy/proxyPort")
- proxy_up = ''
- proxy_port = ''
+ proxy_up = ""
+ proxy_port = ""
- if all([user != '', password != '']):
- proxy_up = f'{user}:{password}@'
+ if all([user != "", password != ""]):
+ proxy_up = f"{user}:{password}@"
- if port != '':
- proxy_port = ':%s' % port
+ if port != "":
+ proxy_port = ":%s" % port
- conn = f'{ptype}://{proxy_up}{host}{proxy_port}'
+ conn = f"{ptype}://{proxy_up}{host}{proxy_port}"
install_opener(build_opener(ProxyHandler({ptype: conn})))
@@ -1005,9 +1098,9 @@ def _get_field_value(field):
value = 0
- if field == 'identifier':
+ if field == "identifier":
value = 0
- if field == 'link':
+ if field == "link":
value = 1
return value
diff --git a/python/plugins/MetaSearch/dialogs/manageconnectionsdialog.py b/python/plugins/MetaSearch/dialogs/manageconnectionsdialog.py
index a34b8ba7a82f..22110dd51e26 100644
--- a/python/plugins/MetaSearch/dialogs/manageconnectionsdialog.py
+++ b/python/plugins/MetaSearch/dialogs/manageconnectionsdialog.py
@@ -29,12 +29,17 @@
import xml.etree.ElementTree as etree
from qgis.core import QgsSettings
-from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QFileDialog, QListWidgetItem, QMessageBox # noqa
+from qgis.PyQt.QtWidgets import (
+ QDialog,
+ QDialogButtonBox,
+ QFileDialog,
+ QListWidgetItem,
+ QMessageBox,
+) # noqa
-from MetaSearch.util import (get_connections_from_file, get_ui_class,
- prettify_xml)
+from MetaSearch.util import get_connections_from_file, get_ui_class, prettify_xml
-BASE_CLASS = get_ui_class('manageconnectionsdialog.ui')
+BASE_CLASS = get_ui_class("manageconnectionsdialog.ui")
class ManageConnectionsDialog(QDialog, BASE_CLASS):
@@ -55,11 +60,15 @@ def manage_gui(self):
"""manage interface"""
if self.mode == 1:
- self.label.setText(self.tr('Load from file'))
- self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(self.tr('Load'))
+ self.label.setText(self.tr("Load from file"))
+ self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(
+ self.tr("Load")
+ )
else:
- self.label.setText(self.tr('Save to file'))
- self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(self.tr('Save'))
+ self.label.setText(self.tr("Save to file"))
+ self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(
+ self.tr("Save")
+ )
self.populate()
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False)
@@ -67,23 +76,25 @@ def manage_gui(self):
def select_file(self):
"""select file ops"""
- label = self.tr('eXtensible Markup Language (*.xml *.XML)')
+ label = self.tr("eXtensible Markup Language (*.xml *.XML)")
if self.mode == 0:
- slabel = self.tr('Save Connections')
- self.filename, filter = QFileDialog.getSaveFileName(self, slabel,
- '.', label)
+ slabel = self.tr("Save Connections")
+ self.filename, filter = QFileDialog.getSaveFileName(
+ self, slabel, ".", label
+ )
else:
- slabel = self.tr('Load Connections')
+ slabel = self.tr("Load Connections")
self.filename, selected_filter = QFileDialog.getOpenFileName(
- self, slabel, '.', label)
+ self, slabel, ".", label
+ )
if not self.filename:
return
# ensure the user never omitted the extension from the file name
- if not self.filename.lower().endswith('.xml'):
- self.filename = '%s.xml' % self.filename
+ if not self.filename.lower().endswith(".xml"):
+ self.filename = "%s.xml" % self.filename
self.leFileName.setText(self.filename)
@@ -96,7 +107,7 @@ def populate(self):
"""populate connections list from settings"""
if self.mode == 0:
- self.settings.beginGroup('/MetaSearch/')
+ self.settings.beginGroup("/MetaSearch/")
keys = self.settings.childGroups()
for key in keys:
item = QListWidgetItem(self.listConnections)
@@ -111,43 +122,46 @@ def populate(self):
self.listConnections.clear()
return
- for catalog in doc.findall('csw'):
+ for catalog in doc.findall("csw"):
item = QListWidgetItem(self.listConnections)
- item.setText(catalog.attrib.get('name'))
+ item.setText(catalog.attrib.get("name"))
def save(self, connections):
"""save connections ops"""
- doc = etree.Element('qgsCSWConnections')
- doc.attrib['version'] = '1.0'
+ doc = etree.Element("qgsCSWConnections")
+ doc.attrib["version"] = "1.0"
for conn in connections:
- url = self.settings.value('/MetaSearch/%s/url' % conn)
- type_ = self.settings.value('/MetaSearch/%s/catalog-type' % conn)
+ url = self.settings.value("/MetaSearch/%s/url" % conn)
+ type_ = self.settings.value("/MetaSearch/%s/catalog-type" % conn)
if url is not None:
- connection = etree.SubElement(doc, 'csw')
- connection.attrib['name'] = conn
- connection.attrib['type'] = type_ or 'OGC CSW 2.0.2'
- connection.attrib['url'] = url
+ connection = etree.SubElement(doc, "csw")
+ connection.attrib["name"] = conn
+ connection.attrib["type"] = type_ or "OGC CSW 2.0.2"
+ connection.attrib["url"] = url
# write to disk
- with open(self.filename, 'w') as fileobj:
+ with open(self.filename, "w") as fileobj:
fileobj.write(prettify_xml(etree.tostring(doc)))
- QMessageBox.information(self, self.tr('Save Connections'),
- self.tr('Saved to {0}.').format(self.filename))
+ QMessageBox.information(
+ self,
+ self.tr("Save Connections"),
+ self.tr("Saved to {0}.").format(self.filename),
+ )
self.reject()
def load(self, items):
"""load connections"""
- self.settings.beginGroup('/MetaSearch/')
+ self.settings.beginGroup("/MetaSearch/")
keys = self.settings.childGroups()
self.settings.endGroup()
exml = etree.parse(self.filename).getroot()
- for catalog in exml.findall('csw'):
- conn_name = catalog.attrib.get('name')
+ for catalog in exml.findall("csw"):
+ conn_name = catalog.attrib.get("name")
# process only selected connections
if conn_name not in items:
@@ -155,19 +169,23 @@ def load(self, items):
# check for duplicates
if conn_name in keys:
- label = self.tr('File {0} exists. Overwrite?').format(
- conn_name)
- res = QMessageBox.warning(self, self.tr('Loading Connections'),
- label,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ label = self.tr("File {0} exists. Overwrite?").format(conn_name)
+ res = QMessageBox.warning(
+ self,
+ self.tr("Loading Connections"),
+ label,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
continue
# no dups detected or overwrite is allowed
- url = '/MetaSearch/%s/url' % conn_name
- type_ = '/MetaSearch/%s/catalog-type' % conn_name
- self.settings.setValue(url, catalog.attrib.get('url'))
- self.settings.setValue(type_, catalog.attrib.get('catalog-type', 'OGC CSW 2.0.2'))
+ url = "/MetaSearch/%s/url" % conn_name
+ type_ = "/MetaSearch/%s/catalog-type" % conn_name
+ self.settings.setValue(url, catalog.attrib.get("url"))
+ self.settings.setValue(
+ type_, catalog.attrib.get("catalog-type", "OGC CSW 2.0.2")
+ )
def accept(self):
"""accept connections"""
diff --git a/python/plugins/MetaSearch/dialogs/newconnectiondialog.py b/python/plugins/MetaSearch/dialogs/newconnectiondialog.py
index fee10ccaeee4..f4eb8e5d90cd 100644
--- a/python/plugins/MetaSearch/dialogs/newconnectiondialog.py
+++ b/python/plugins/MetaSearch/dialogs/newconnectiondialog.py
@@ -32,7 +32,7 @@
from MetaSearch.util import get_ui_class
from MetaSearch.search_backend import CATALOG_TYPES
-BASE_CLASS = get_ui_class('newconnectiondialog.ui')
+BASE_CLASS = get_ui_class("newconnectiondialog.ui")
class NewConnectionDialog(QDialog, BASE_CLASS):
@@ -60,49 +60,53 @@ def accept(self):
conn_password = self.lePassword.text().strip()
conn_catalog_type = self.cmbCatalogType.currentText()
- if any([conn_name == '', conn_url == '']):
- QMessageBox.warning(self, self.tr('Save Connection'),
- self.tr('Both Name and URL must be provided.'))
+ if any([conn_name == "", conn_url == ""]):
+ QMessageBox.warning(
+ self,
+ self.tr("Save Connection"),
+ self.tr("Both Name and URL must be provided."),
+ )
return
- if '/' in conn_name:
- QMessageBox.warning(self, self.tr('Save Connection'),
- self.tr('Name cannot contain \'/\'.'))
+ if "/" in conn_name:
+ QMessageBox.warning(
+ self, self.tr("Save Connection"), self.tr("Name cannot contain '/'.")
+ )
return
if conn_name is not None:
- key = '/MetaSearch/%s' % conn_name
- keyurl = '%s/url' % key
- key_orig = '/MetaSearch/%s' % self.conn_name_orig
+ key = "/MetaSearch/%s" % conn_name
+ keyurl = "%s/url" % key
+ key_orig = "/MetaSearch/%s" % self.conn_name_orig
# warn if entry was renamed to an existing connection
- if all([self.conn_name_orig != conn_name,
- self.settings.contains(keyurl)]):
+ if all([self.conn_name_orig != conn_name, self.settings.contains(keyurl)]):
res = QMessageBox.warning(
- self, self.tr('Save Connection'),
- self.tr('Overwrite {0}?').format(conn_name),
- QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel)
+ self,
+ self.tr("Save Connection"),
+ self.tr("Overwrite {0}?").format(conn_name),
+ QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel,
+ )
if res == QMessageBox.StandardButton.Cancel:
return
# on rename delete original entry first
- if all([self.conn_name_orig is not None,
- self.conn_name_orig != conn_name]):
+ if all([self.conn_name_orig is not None, self.conn_name_orig != conn_name]):
self.settings.remove(key_orig)
self.settings.setValue(keyurl, conn_url)
- self.settings.setValue('/MetaSearch/selected', conn_name)
+ self.settings.setValue("/MetaSearch/selected", conn_name)
- if conn_username != '':
- self.settings.setValue('%s/username' % key, conn_username)
+ if conn_username != "":
+ self.settings.setValue("%s/username" % key, conn_username)
else:
- self.settings.remove('%s/username' % key)
- if conn_password != '':
- self.settings.setValue('%s/password' % key, conn_password)
+ self.settings.remove("%s/username" % key)
+ if conn_password != "":
+ self.settings.setValue("%s/password" % key, conn_password)
else:
- self.settings.remove('%s/password' % key)
+ self.settings.remove("%s/password" % key)
- self.settings.setValue('%s/catalog-type' % key, conn_catalog_type)
+ self.settings.setValue("%s/catalog-type" % key, conn_catalog_type)
QDialog.accept(self)
diff --git a/python/plugins/MetaSearch/dialogs/recorddialog.py b/python/plugins/MetaSearch/dialogs/recorddialog.py
index 9c9f9b9e00b5..89bfe9f00a7f 100644
--- a/python/plugins/MetaSearch/dialogs/recorddialog.py
+++ b/python/plugins/MetaSearch/dialogs/recorddialog.py
@@ -30,7 +30,7 @@
from MetaSearch.util import get_ui_class
-BASE_CLASS = get_ui_class('recorddialog.ui')
+BASE_CLASS = get_ui_class("recorddialog.ui")
class RecordDialog(QDialog, BASE_CLASS):
diff --git a/python/plugins/MetaSearch/link_types.py b/python/plugins/MetaSearch/link_types.py
index 66dce8d983ec..2a0647dc2ce6 100644
--- a/python/plugins/MetaSearch/link_types.py
+++ b/python/plugins/MetaSearch/link_types.py
@@ -21,46 +21,37 @@
###############################################################################
WMSWMST_LINK_TYPES = [
- 'WMS',
- 'WMTS',
- 'OGC:WMS',
- 'OGC:WMTS',
- 'OGC:WMS-1.1.1-http-get-map',
- 'OGC:WMS-1.1.1-http-get-capabilities',
- 'OGC:WMS-1.3.0-http-get-map',
- 'OGC:WMS-1.3.0-http-get-capabilities',
- 'urn:x-esri:specification:ServiceType:wms:url',
- 'urn:x-esri:specification:ServiceType:Gmd:URL.wms'
+ "WMS",
+ "WMTS",
+ "OGC:WMS",
+ "OGC:WMTS",
+ "OGC:WMS-1.1.1-http-get-map",
+ "OGC:WMS-1.1.1-http-get-capabilities",
+ "OGC:WMS-1.3.0-http-get-map",
+ "OGC:WMS-1.3.0-http-get-capabilities",
+ "urn:x-esri:specification:ServiceType:wms:url",
+ "urn:x-esri:specification:ServiceType:Gmd:URL.wms",
]
WFS_LINK_TYPES = [
- 'WFS',
- 'OGC:WFS',
- 'OGC:WFS-1.0.0-http-get-capabilities',
- 'OGC:WFS-1.1.0-http-get-capabilities',
- 'urn:x-esri:specification:ServiceType:wfs:url',
- 'urn:x-esri:specification:ServiceType:Gmd:URL.wfs'
+ "WFS",
+ "OGC:WFS",
+ "OGC:WFS-1.0.0-http-get-capabilities",
+ "OGC:WFS-1.1.0-http-get-capabilities",
+ "urn:x-esri:specification:ServiceType:wfs:url",
+ "urn:x-esri:specification:ServiceType:Gmd:URL.wfs",
]
WCS_LINK_TYPES = [
- 'WCS',
- 'OGC:WCS',
- 'OGC:WCS-1.1.0-http-get-capabilities',
- 'urn:x-esri:specification:ServiceType:wcs:url',
- 'urn:x-esri:specification:ServiceType:Gmd:URL.wcs'
+ "WCS",
+ "OGC:WCS",
+ "OGC:WCS-1.1.0-http-get-capabilities",
+ "urn:x-esri:specification:ServiceType:wcs:url",
+ "urn:x-esri:specification:ServiceType:Gmd:URL.wcs",
]
-AMS_LINK_TYPES = [
- 'ESRI:ArcGIS:MapServer',
- 'Esri REST: Map Service',
- 'ESRI REST'
-]
+AMS_LINK_TYPES = ["ESRI:ArcGIS:MapServer", "Esri REST: Map Service", "ESRI REST"]
-AFS_LINK_TYPES = [
- 'ESRI:ArcGIS:FeatureServer',
- 'Esri REST: Feature Service'
-]
+AFS_LINK_TYPES = ["ESRI:ArcGIS:FeatureServer", "Esri REST: Feature Service"]
-GIS_FILE_LINK_TYPES = [
- 'FILE:GEO'
-]
+GIS_FILE_LINK_TYPES = ["FILE:GEO"]
diff --git a/python/plugins/MetaSearch/pavement.py b/python/plugins/MetaSearch/pavement.py
index 2fc7476ceafb..6cfeed6f83b6 100644
--- a/python/plugins/MetaSearch/pavement.py
+++ b/python/plugins/MetaSearch/pavement.py
@@ -26,35 +26,30 @@
import xmlrpc.client
import zipfile
-from paver.easy import (call_task, cmdopts, error, info, options, path,
- sh, task, Bunch)
+from paver.easy import call_task, cmdopts, error, info, options, path, sh, task, Bunch
from owslib.csw import CatalogueServiceWeb # spellok
-PLUGIN_NAME = 'MetaSearch'
+PLUGIN_NAME = "MetaSearch"
BASEDIR = os.path.abspath(os.path.dirname(__file__))
-USERDIR = os.path.expanduser('~')
+USERDIR = os.path.expanduser("~")
-with open('metadata.txt') as mf:
+with open("metadata.txt") as mf:
cp = ConfigParser()
cp.readfp(mf)
- VERSION = cp.get('general', 'version')
+ VERSION = cp.get("general", "version")
options(
base=Bunch(
home=BASEDIR,
plugin=path(BASEDIR),
- ui=path(BASEDIR) / 'plugin' / PLUGIN_NAME / 'ui',
- install=path('%s/.qgis3/python/plugins/MetaSearch' % USERDIR),
- ext_libs=path('plugin/MetaSearch/ext-libs'),
- tmp=path(path('%s/MetaSearch-dist' % USERDIR)),
- version=VERSION
+ ui=path(BASEDIR) / "plugin" / PLUGIN_NAME / "ui",
+ install=path("%s/.qgis3/python/plugins/MetaSearch" % USERDIR),
+ ext_libs=path("plugin/MetaSearch/ext-libs"),
+ tmp=path(path("%s/MetaSearch-dist" % USERDIR)),
+ version=VERSION,
),
- upload=Bunch(
- host='plugins.qgis.org',
- port=80,
- endpoint='plugins/RPC2/'
- )
+ upload=Bunch(host="plugins.qgis.org", port=80, endpoint="plugins/RPC2/"),
)
@@ -72,17 +67,17 @@ def clean():
if os.path.exists(options.base.ext_libs):
shutil.rmtree(options.base.ext_libs)
for ui_file in os.listdir(options.base.ui):
- if ui_file.endswith('.py') and ui_file != '__init__.py':
- os.remove(options.base.plugin / 'ui' / ui_file)
- os.remove(path(options.base.home) / '%s.pro' % PLUGIN_NAME)
- sh('git clean -dxf')
+ if ui_file.endswith(".py") and ui_file != "__init__.py":
+ os.remove(options.base.plugin / "ui" / ui_file)
+ os.remove(path(options.base.home) / "%s.pro" % PLUGIN_NAME)
+ sh("git clean -dxf")
@task
def install():
"""install plugin into user QGIS environment"""
- plugins_dir = path(USERDIR) / '.qgis3/python/plugins'
+ plugins_dir = path(USERDIR) / ".qgis3/python/plugins"
if os.path.exists(options.base.install):
if os.path.islink(options.base.install):
@@ -91,8 +86,8 @@ def install():
shutil.rmtree(options.base.install)
if not os.path.exists(plugins_dir):
- raise OSError('The directory %s does not exist.' % plugins_dir)
- if not hasattr(os, 'symlink'):
+ raise OSError("The directory %s does not exist." % plugins_dir)
+ if not hasattr(os, "symlink"):
shutil.copytree(options.base.plugin, options.base.install)
elif not os.path.exists(options.base.install):
os.symlink(options.base.plugin, options.base.install)
@@ -103,11 +98,11 @@ def package():
"""create zip file of plugin"""
skip_files = [
- 'AUTHORS.txt',
- 'CMakeLists.txt',
- 'requirements.txt',
- 'requirements-dev.txt',
- 'pavement.txt'
+ "AUTHORS.txt",
+ "CMakeLists.txt",
+ "requirements.txt",
+ "requirements-dev.txt",
+ "pavement.txt",
]
package_file = get_package_filename()
@@ -116,10 +111,10 @@ def package():
options.base.tmp.mkdir()
if os.path.exists(package_file):
os.unlink(package_file)
- with zipfile.ZipFile(package_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
+ with zipfile.ZipFile(package_file, "w", zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(options.base.plugin):
for file_add in files:
- if file_add.endswith('.pyc') or file_add in skip_files:
+ if file_add.endswith(".pyc") or file_add in skip_files:
continue
filepath = os.path.join(root, file_add)
relpath = os.path.join(PLUGIN_NAME, os.path.relpath(filepath))
@@ -128,81 +123,90 @@ def package():
@task
-@cmdopts([
- ('user=', 'u', 'OSGeo userid'),
-])
+@cmdopts(
+ [
+ ("user=", "u", "OSGeo userid"),
+ ]
+)
def upload():
"""upload package zipfile to server"""
- user = options.get('user', False)
+ user = options.get("user", False)
if not user:
- raise ValueError('OSGeo userid required')
+ raise ValueError("OSGeo userid required")
- password = getpass.getpass('Enter your password: ')
- if password.strip() == '':
- raise ValueError('password required')
+ password = getpass.getpass("Enter your password: ")
+ if password.strip() == "":
+ raise ValueError("password required")
- call_task('package')
+ call_task("package")
zipf = get_package_filename()
- url = 'http://%s:%s@%s:%d/%s' % (user, password, options.upload.host,
- options.upload.port,
- options.upload.endpoint)
+ url = "http://%s:%s@%s:%d/%s" % (
+ user,
+ password,
+ options.upload.host,
+ options.upload.port,
+ options.upload.endpoint,
+ )
- info('Uploading to http://{}/{}'.format(options.upload.host, options.upload.endpoint))
+ info(f"Uploading to http://{options.upload.host}/{options.upload.endpoint}")
server = xmlrpc.client.ServerProxy(url, verbose=False)
try:
with open(zipf) as zfile:
- plugin_id, version_id = \
- server.plugin.upload(xmlrpc.client.Binary(zfile.read()))
- info('Plugin ID: %s', plugin_id)
- info('Version ID: %s', version_id)
+ plugin_id, version_id = server.plugin.upload(
+ xmlrpc.client.Binary(zfile.read())
+ )
+ info("Plugin ID: %s", plugin_id)
+ info("Version ID: %s", version_id)
except xmlrpc.client.Fault as err:
- error('ERROR: fault error')
- error('Fault code: %d', err.faultCode)
- error('Fault string: %s', err.faultString)
+ error("ERROR: fault error")
+ error("Fault code: %d", err.faultCode)
+ error("Fault string: %s", err.faultString)
except xmlrpc.client.ProtocolError as err:
- error('Error: Protocol error')
+ error("Error: Protocol error")
error("%s : %s", err.errcode, err.errmsg)
if err.errcode == 403:
- error('Invalid name and password')
+ error("Invalid name and password")
@task
def test_default_csw_connections():
"""test that the default CSW connections work"""
- relpath = 'resources%sconnections-default.xml' % os.sep
+ relpath = "resources%sconnections-default.xml" % os.sep
csw_connections_xml = options.base.plugin / relpath
conns = etree.parse(csw_connections_xml)
- for conn in conns.findall('csw'):
+ for conn in conns.findall("csw"):
try:
- csw = CatalogueServiceWeb(conn.attrib.get('url')) # spellok
- info('Success: %s', csw.identification.title)
+ csw = CatalogueServiceWeb(conn.attrib.get("url")) # spellok
+ info("Success: %s", csw.identification.title)
csw.getrecords2()
except Exception as err:
- raise ValueError('ERROR: %s', err)
+ raise ValueError("ERROR: %s", err)
@task
-@cmdopts([
- ('filename=', 'f', 'Path to file of CSW URLs'),
-])
+@cmdopts(
+ [
+ ("filename=", "f", "Path to file of CSW URLs"),
+ ]
+)
def generate_csw_connections_file():
"""generate a CSW connections file from a flat file of CSW URLs"""
- filename = options.get('filename', False)
+ filename = options.get("filename", False)
if not filename:
- raise ValueError('path to file of CSW URLs required')
+ raise ValueError("path to file of CSW URLs required")
- conns = etree.Element('qgsCSWConnections')
- conns.attrib['version'] = '1.0'
+ conns = etree.Element("qgsCSWConnections")
+ conns.attrib["version"] = "1.0"
with open(filename) as connsfh:
for line in connsfh:
@@ -212,17 +216,17 @@ def generate_csw_connections_file():
try:
csw = CatalogueServiceWeb(url) # spellok
title = str(csw.identification.title)
- etree.SubElement(conns, 'csw', name=title, url=url)
+ etree.SubElement(conns, "csw", name=title, url=url)
except Exception as err:
- error('ERROR on CSW %s: %s', url, err)
+ error("ERROR on CSW %s: %s", url, err)
- with open('%s.xml' % filename, 'w') as connsxmlfh:
- connsxmlfh.write(etree.tostring(conns, encoding='utf-8'))
+ with open("%s.xml" % filename, "w") as connsxmlfh:
+ connsxmlfh.write(etree.tostring(conns, encoding="utf-8"))
def get_package_filename():
"""return filepath of plugin zipfile"""
- filename = f'{PLUGIN_NAME}-{options.base.version}.zip'
- package_file = f'{options.base.tmp}/{filename}'
+ filename = f"{PLUGIN_NAME}-{options.base.version}.zip"
+ package_file = f"{options.base.tmp}/{filename}"
return package_file
diff --git a/python/plugins/MetaSearch/plugin.py b/python/plugins/MetaSearch/plugin.py
index d131c4b7cc96..7e4c0c0db528 100644
--- a/python/plugins/MetaSearch/plugin.py
+++ b/python/plugins/MetaSearch/plugin.py
@@ -44,21 +44,22 @@ def __init__(self, iface):
self.action_run = None
self.action_help = None
self.dialog = None
- self.web_menu = '&MetaSearch'
+ self.web_menu = "&MetaSearch"
def initGui(self):
"""startup"""
# run
- log_message('Initializing plugin', Qgis.MessageLevel.Info)
+ log_message("Initializing plugin", Qgis.MessageLevel.Info)
- run_icon = QIcon('{}/{}'.format(self.context.ppath, 'images/MetaSearch.svg'))
- self.action_run = QAction(run_icon, 'MetaSearch',
- self.iface.mainWindow())
+ run_icon = QIcon("{}/{}".format(self.context.ppath, "images/MetaSearch.svg"))
+ self.action_run = QAction(run_icon, "MetaSearch", self.iface.mainWindow())
self.action_run.setWhatsThis(
- QCoreApplication.translate('MetaSearch', 'MetaSearch plugin'))
- self.action_run.setStatusTip(QCoreApplication.translate(
- 'MetaSearch', 'Search Metadata Catalogs'))
+ QCoreApplication.translate("MetaSearch", "MetaSearch plugin")
+ )
+ self.action_run.setStatusTip(
+ QCoreApplication.translate("MetaSearch", "Search Metadata Catalogs")
+ )
self.action_run.triggered.connect(self.run)
@@ -66,12 +67,14 @@ def initGui(self):
self.iface.addPluginToWebMenu(self.web_menu, self.action_run)
# help
- help_icon = QgsApplication.getThemeIcon('/mActionHelpContents.svg')
- self.action_help = QAction(help_icon, 'Help', self.iface.mainWindow())
+ help_icon = QgsApplication.getThemeIcon("/mActionHelpContents.svg")
+ self.action_help = QAction(help_icon, "Help", self.iface.mainWindow())
self.action_help.setWhatsThis(
- QCoreApplication.translate('MetaSearch', 'MetaSearch plugin help'))
- self.action_help.setStatusTip(QCoreApplication.translate(
- 'MetaSearch', 'Get Help on MetaSearch'))
+ QCoreApplication.translate("MetaSearch", "MetaSearch plugin help")
+ )
+ self.action_help.setStatusTip(
+ QCoreApplication.translate("MetaSearch", "Get Help on MetaSearch")
+ )
self.action_help.triggered.connect(self.help)
self.iface.addPluginToWebMenu(self.web_menu, self.action_help)
@@ -82,7 +85,7 @@ def initGui(self):
def unload(self):
"""teardown"""
- log_message('Unloading plugin', Qgis.MessageLevel.Info)
+ log_message("Unloading plugin", Qgis.MessageLevel.Info)
# remove the plugin menu item and icon
self.iface.removePluginWebMenu(self.web_menu, self.action_run)
@@ -92,7 +95,7 @@ def unload(self):
def run(self):
"""open MetaSearch"""
- log_message('Running plugin', Qgis.MessageLevel.Info)
+ log_message("Running plugin", Qgis.MessageLevel.Info)
self.dialog.exec()
diff --git a/python/plugins/MetaSearch/search_backend.py b/python/plugins/MetaSearch/search_backend.py
index 1b461fec19bb..99ba387e8307 100644
--- a/python/plugins/MetaSearch/search_backend.py
+++ b/python/plugins/MetaSearch/search_backend.py
@@ -36,15 +36,12 @@
from qgis.core import Qgis
-if owslib.__version__ < '0.25':
+if owslib.__version__ < "0.25":
OWSLIB_OAREC_SUPPORTED = False
else:
OWSLIB_OAREC_SUPPORTED = True
-CATALOG_TYPES = [
- 'OGC CSW 2.0.2',
- 'OGC API - Records'
-]
+CATALOG_TYPES = ["OGC CSW 2.0.2", "OGC API - Records"]
class SearchBase:
@@ -83,17 +80,19 @@ def __init__(self, url, timeout, username, password, auth):
super().__init__(url, timeout, username, password, auth)
self.type = CATALOG_TYPES[0]
- self.format = 'xml'
- self.service_info_template = 'csw_service_metadata.html'
- self.record_info_template = 'record_metadata_dc.html'
+ self.format = "xml"
+ self.service_info_template = "csw_service_metadata.html"
+ self.record_info_template = "record_metadata_dc.html"
self.constraints = []
- log_message(f'Connecting to CSW: {self.url}', Qgis.MessageLevel.Info)
- self.conn = CatalogueServiceWeb(self.url, # spellok
- timeout=self.timeout,
- username=self.username,
- password=self.password,
- auth=self.auth)
+ log_message(f"Connecting to CSW: {self.url}", Qgis.MessageLevel.Info)
+ self.conn = CatalogueServiceWeb(
+ self.url, # spellok
+ timeout=self.timeout,
+ username=self.username,
+ password=self.password,
+ auth=self.auth,
+ )
self.request = self.conn.request
self.response = self.conn.response
@@ -105,29 +104,36 @@ def query_records(self, bbox=[], keywords=None, limit=10, offset=1):
# only apply spatial filter if bbox is not global
# even for a global bbox, if a spatial filter is applied, then
# the CSW server will skip records without a bbox
- if bbox and bbox != ['-180', '-90', '180', '90']:
- log_message(f'Setting bbox filter ({bbox})', Qgis.MessageLevel.Info)
+ if bbox and bbox != ["-180", "-90", "180", "90"]:
+ log_message(f"Setting bbox filter ({bbox})", Qgis.MessageLevel.Info)
minx, miny, maxx, maxy = bbox
- self.constraints.append(BBox([miny, minx, maxy, maxx],
- crs='urn:ogc:def:crs:EPSG::4326'))
+ self.constraints.append(
+ BBox([miny, minx, maxy, maxx], crs="urn:ogc:def:crs:EPSG::4326")
+ )
# keywords
if keywords:
# TODO: handle multiple word searches
- log_message(f'Setting csw:AnyText filter {keywords}', Qgis.MessageLevel.Info)
- self.constraints.append(PropertyIsLike('csw:AnyText', keywords))
+ log_message(
+ f"Setting csw:AnyText filter {keywords}", Qgis.MessageLevel.Info
+ )
+ self.constraints.append(PropertyIsLike("csw:AnyText", keywords))
if len(self.constraints) > 1: # exclusive search (a && b)
self.constraints = [self.constraints]
- log_message('Searching CSW: {self.url}', Qgis.MessageLevel.Info)
- self.conn.getrecords2(constraints=self.constraints, maxrecords=limit,
- startposition=offset, esn='full')
+ log_message("Searching CSW: {self.url}", Qgis.MessageLevel.Info)
+ self.conn.getrecords2(
+ constraints=self.constraints,
+ maxrecords=limit,
+ startposition=offset,
+ esn="full",
+ )
- self.matches = self.conn.results['matches']
- self.returned = self.conn.results['returned']
- log_message(f'Matches: {self.matches}', Qgis.MessageLevel.Info)
- log_message(f'Returned: {self.returned}', Qgis.MessageLevel.Info)
+ self.matches = self.conn.results["matches"]
+ self.returned = self.conn.results["returned"]
+ log_message(f"Matches: {self.matches}", Qgis.MessageLevel.Info)
+ log_message(f"Returned: {self.returned}", Qgis.MessageLevel.Info)
self.request = self.conn.request
self.response = self.conn.response
@@ -136,32 +142,27 @@ def records(self):
recs = []
for record in self.conn.records:
- rec = {
- 'identifier': None,
- 'type': None,
- 'title': None,
- 'bbox': None
- }
+ rec = {"identifier": None, "type": None, "title": None, "bbox": None}
if self.conn.records[record].identifier:
- rec['identifier'] = self.conn.records[record].identifier
+ rec["identifier"] = self.conn.records[record].identifier
if self.conn.records[record].type:
- rec['type'] = self.conn.records[record].type
+ rec["type"] = self.conn.records[record].type
if self.conn.records[record].title:
- rec['title'] = self.conn.records[record].title
+ rec["title"] = self.conn.records[record].title
if self.conn.records[record].bbox:
- rec['bbox'] = bbox_list_to_dict(
- self.conn.records[record].bbox)
+ rec["bbox"] = bbox_list_to_dict(self.conn.records[record].bbox)
- rec['links'] = (self.conn.records[record].uris +
- self.conn.records[record].references)
+ rec["links"] = (
+ self.conn.records[record].uris + self.conn.records[record].references
+ )
recs.append(rec)
return recs
def get_record(self, identifier):
- log_message(f'Searching CSW for record: {identifier}', Qgis.MessageLevel.Info)
+ log_message(f"Searching CSW for record: {identifier}", Qgis.MessageLevel.Info)
self.conn.getrecordbyid([identifier])
return self.conn.records[identifier]
@@ -178,27 +179,28 @@ def __init__(self, url, timeout, auth):
super().__init__(url, timeout, auth)
self.type = CATALOG_TYPES[1]
- self.format = 'json'
- self.service_info_template = 'oarec_service_metadata.html'
- self.record_info_template = 'record_metadata_oarec.html'
+ self.format = "json"
+ self.service_info_template = "oarec_service_metadata.html"
+ self.record_info_template = "record_metadata_oarec.html"
self.base_url = None
self.record_collection = None
- if '/collections/' in self.url: # catalog is a collection
- log_message('OARec endpoint is a collection', Qgis.MessageLevel.Info)
- self.base_url, self.record_collection = self.url.split('/collections/') # noqa
- self.conn = Records(
- self.base_url, timeout=self.timeout, auth=self.auth)
+ if "/collections/" in self.url: # catalog is a collection
+ log_message("OARec endpoint is a collection", Qgis.MessageLevel.Info)
+ self.base_url, self.record_collection = self.url.split(
+ "/collections/"
+ ) # noqa
+ self.conn = Records(self.base_url, timeout=self.timeout, auth=self.auth)
c = self.conn.collection(self.record_collection)
try:
- self.conn.links = c['links']
- self.conn.title = c['title']
- self.conn.description = c['description']
+ self.conn.links = c["links"]
+ self.conn.title = c["title"]
+ self.conn.description = c["description"]
except KeyError:
pass
self.request = self.conn.request
else:
- log_message('OARec endpoint is not a collection', Qgis.MessageLevel.Info)
+ log_message("OARec endpoint is not a collection", Qgis.MessageLevel.Info)
self.conn = Records(self.url, timeout=self.timeout, auth=self.auth)
self.request = None
@@ -210,47 +212,49 @@ def query_records(self, bbox=[], keywords=None, limit=10, offset=1):
offset2 = offset - 1
params = {
- 'collection_id': self.record_collection,
- 'limit': limit,
- 'offset': offset2
+ "collection_id": self.record_collection,
+ "limit": limit,
+ "offset": offset2,
}
if keywords:
- log_message(f'Setting keyword search {keywords}', Qgis.MessageLevel.Info)
- params['q'] = keywords
- if bbox and bbox != ['-180', '-90', '180', '90']:
- log_message(f'Setting bbox search {bbox}', Qgis.MessageLevel.Info)
- params['bbox'] = bbox
+ log_message(f"Setting keyword search {keywords}", Qgis.MessageLevel.Info)
+ params["q"] = keywords
+ if bbox and bbox != ["-180", "-90", "180", "90"]:
+ log_message(f"Setting bbox search {bbox}", Qgis.MessageLevel.Info)
+ params["bbox"] = bbox
- log_message(f'Searching OARec: {self.url}', Qgis.MessageLevel.Info)
+ log_message(f"Searching OARec: {self.url}", Qgis.MessageLevel.Info)
self.response = self.conn.collection_items(**params)
- self.matches = self.response.get('numberMatched', 0)
- self.returned = self.response.get('numberReturned', 0)
+ self.matches = self.response.get("numberMatched", 0)
+ self.returned = self.response.get("numberReturned", 0)
self.request = self.conn.request
- log_message(f'Matches: {self.matches}', Qgis.MessageLevel.Info)
- log_message(f'Returned: {self.returned}', Qgis.MessageLevel.Info)
+ log_message(f"Matches: {self.matches}", Qgis.MessageLevel.Info)
+ log_message(f"Returned: {self.returned}", Qgis.MessageLevel.Info)
def records(self):
recs = []
- for rec in self.response['features']:
+ for rec in self.response["features"]:
rec1 = {
- 'identifier': rec['id'],
- 'type': rec['properties']['type'],
- 'bbox': None,
- 'title': rec['properties']['title'],
- 'links': rec.get('links', [])
+ "identifier": rec["id"],
+ "type": rec["properties"]["type"],
+ "bbox": None,
+ "title": rec["properties"]["title"],
+ "links": rec.get("links", []),
}
try:
- if rec.get('geometry') is not None:
- rec1['bbox'] = bbox_list_to_dict([
- rec['geometry']['coordinates'][0][0][0],
- rec['geometry']['coordinates'][0][0][1],
- rec['geometry']['coordinates'][0][2][0],
- rec['geometry']['coordinates'][0][2][1]
- ])
+ if rec.get("geometry") is not None:
+ rec1["bbox"] = bbox_list_to_dict(
+ [
+ rec["geometry"]["coordinates"][0][0][0],
+ rec["geometry"]["coordinates"][0][0][1],
+ rec["geometry"]["coordinates"][0][2][0],
+ rec["geometry"]["coordinates"][0][2][1],
+ ]
+ )
except KeyError:
pass
@@ -259,30 +263,30 @@ def records(self):
return recs
def get_record(self, identifier):
- log_message(f'Searching OARec endpoint for item {identifier}',
- Qgis.MessageLevel.Info)
+ log_message(
+ f"Searching OARec endpoint for item {identifier}", Qgis.MessageLevel.Info
+ )
return self.conn.collection_item(self.record_collection, identifier)
def parse_link(self, link):
link2 = {}
- if 'href' in link:
- link2['url'] = link['href']
- if 'type' in link:
- link2['protocol'] = link['type']
- if 'title' in link:
- link2['title'] = link['title']
- if 'id' in link:
- link2['name'] = link['id']
+ if "href" in link:
+ link2["url"] = link["href"]
+ if "type" in link:
+ link2["protocol"] = link["type"]
+ if "title" in link:
+ link2["title"] = link["title"]
+ if "id" in link:
+ link2["name"] = link["id"]
return link2
-def get_catalog_service(url, catalog_type, timeout, username, password,
- auth=None):
+def get_catalog_service(url, catalog_type, timeout, username, password, auth=None):
if catalog_type in [None, CATALOG_TYPES[0]]:
- log_message('CSW endpoint detected', Qgis.MessageLevel.Info)
+ log_message("CSW endpoint detected", Qgis.MessageLevel.Info)
return CSW202Search(url, timeout, username, password, auth)
elif catalog_type == CATALOG_TYPES[1]:
- log_message('OARec endpoint detected', Qgis.MessageLevel.Info)
+ log_message("OARec endpoint detected", Qgis.MessageLevel.Info)
if not OWSLIB_OAREC_SUPPORTED:
raise ValueError("OGC API - Records requires OWSLib 0.25 or above")
return OARecSearch(url, timeout, auth)
@@ -290,17 +294,12 @@ def get_catalog_service(url, catalog_type, timeout, username, password,
def bbox_list_to_dict(bbox):
if isinstance(bbox, list):
- dict_ = {
- 'minx': bbox[0],
- 'maxx': bbox[2],
- 'miny': bbox[1],
- 'maxy': bbox[3]
- }
+ dict_ = {"minx": bbox[0], "maxx": bbox[2], "miny": bbox[1], "maxy": bbox[3]}
else:
dict_ = {
- 'minx': bbox.minx,
- 'maxx': bbox.maxx,
- 'miny': bbox.miny,
- 'maxy': bbox.maxy
+ "minx": bbox.minx,
+ "maxx": bbox.maxx,
+ "miny": bbox.miny,
+ "maxy": bbox.maxy,
}
return dict_
diff --git a/python/plugins/MetaSearch/util.py b/python/plugins/MetaSearch/util.py
index 7f6345baf06a..a939604f5809 100644
--- a/python/plugins/MetaSearch/util.py
+++ b/python/plugins/MetaSearch/util.py
@@ -53,18 +53,21 @@ def __init__(self):
def get_ui_class(ui_file):
"""return class object of a uifile"""
- ui_file_full = '{}{}ui{}{}'.format(os.path.dirname(os.path.abspath(__file__)), os.sep, os.sep, ui_file)
+ ui_file_full = (
+ f"{os.path.dirname(os.path.abspath(__file__))}{os.sep}ui{os.sep}{ui_file}"
+ )
return loadUiType(ui_file_full)[0]
def render_template(language, context, data, template):
"""Renders HTML display of raw API request/response/content"""
- env = Environment(extensions=['jinja2.ext.i18n'],
- loader=FileSystemLoader(context.ppath))
+ env = Environment(
+ extensions=["jinja2.ext.i18n"], loader=FileSystemLoader(context.ppath)
+ )
env.install_gettext_callables(gettext, ngettext, newstyle=True)
- template_file = 'resources/templates/%s' % template
+ template_file = "resources/templates/%s" % template
template = env.get_template(template_file)
return template.render(language=language, obj=data)
@@ -75,18 +78,18 @@ def get_connections_from_file(parent, filename):
error = 0
try:
doc = etree.parse(filename).getroot()
- if doc.tag != 'qgsCSWConnections':
+ if doc.tag != "qgsCSWConnections":
error = 1
- msg = parent.tr('Invalid Catalog connections XML.')
+ msg = parent.tr("Invalid Catalog connections XML.")
except etree.ParseError as err:
error = 1
- msg = parent.tr('Cannot parse XML file: {0}').format(err)
+ msg = parent.tr("Cannot parse XML file: {0}").format(err)
except OSError as err:
error = 1
- msg = parent.tr('Cannot open file: {0}').format(err)
+ msg = parent.tr("Cannot open file: {0}").format(err)
if error == 1:
- QMessageBox.information(parent, parent.tr('Loading Connections'), msg)
+ QMessageBox.information(parent, parent.tr("Loading Connections"), msg)
return
return doc
@@ -95,13 +98,13 @@ def prettify_xml(xml):
"""convenience function to prettify XML"""
if isinstance(xml, bytes):
- xml = xml.decode('utf-8')
+ xml = xml.decode("utf-8")
- if xml.count('\n') > 20: # likely already pretty printed
+ if xml.count("\n") > 20: # likely already pretty printed
return xml
# check if it's a GET request
- if xml.startswith('http'):
+ if xml.startswith("http"):
return xml
else:
return parseString(xml).toprettyxml()
@@ -110,17 +113,17 @@ def prettify_xml(xml):
def get_help_url():
"""return QGIS MetaSearch help documentation link"""
- locale_name = QgsSettings().value('locale/userLocale')[0:2]
- major, minor = Qgis.QGIS_VERSION.split('.')[:2]
+ locale_name = QgsSettings().value("locale/userLocale")[0:2]
+ major, minor = Qgis.QGIS_VERSION.split(".")[:2]
- if minor == '99': # master
- version = 'testing'
+ if minor == "99": # master
+ version = "testing"
else:
- version = '.'.join([major, minor])
+ version = ".".join([major, minor])
- path = f'{version}/{locale_name}/docs/user_manual/plugins/core_plugins/plugins_metasearch.html' # noqa
+ path = f"{version}/{locale_name}/docs/user_manual/plugins/core_plugins/plugins_metasearch.html" # noqa
- return '/'.join(['https://docs.qgis.org', path])
+ return "/".join(["https://docs.qgis.org", path])
def open_url(url):
@@ -132,7 +135,7 @@ def open_url(url):
def normalize_text(text):
"""tidy up string"""
- return text.replace('\n', '')
+ return text.replace("\n", "")
def serialize_string(input_string):
@@ -141,12 +144,12 @@ def serialize_string(input_string):
s = input_string.strip().split()
last_token = s[-1]
- all_other_tokens_as_string = input_string.replace(last_token, '')
+ all_other_tokens_as_string = input_string.replace(last_token, "")
if last_token.isdigit():
- value = f'{all_other_tokens_as_string}{int(last_token) + 1}'
+ value = f"{all_other_tokens_as_string}{int(last_token) + 1}"
else:
- value = '%s 1' % input_string
+ value = "%s 1" % input_string
return value
@@ -159,10 +162,10 @@ def clean_ows_url(url):
if query_string:
query_string = QUrlQuery(query_string)
- query_string.removeQueryItem('service')
- query_string.removeQueryItem('SERVICE')
- query_string.removeQueryItem('request')
- query_string.removeQueryItem('REQUEST')
+ query_string.removeQueryItem("service")
+ query_string.removeQueryItem("SERVICE")
+ query_string.removeQueryItem("request")
+ query_string.removeQueryItem("REQUEST")
url.setQuery(query_string)
return url.toString()
@@ -171,4 +174,4 @@ def clean_ows_url(url):
def log_message(message, level=Qgis.MessageLevel.Info):
"""helper function to emit logging messages"""
- LOGGER.logMessage(message, 'MetaSearch', level)
+ LOGGER.logMessage(message, "MetaSearch", level)
diff --git a/python/plugins/db_manager/db_manager.py b/python/plugins/db_manager/db_manager.py
index 5562d023b8e0..cbae74596beb 100644
--- a/python/plugins/db_manager/db_manager.py
+++ b/python/plugins/db_manager/db_manager.py
@@ -23,16 +23,25 @@
import functools
from qgis.PyQt.QtCore import Qt, QByteArray, QSize
-from qgis.PyQt.QtWidgets import QAction, QMainWindow, QApplication, QMenu, QTabWidget, QGridLayout, QSpacerItem, QSizePolicy, QDockWidget, QStatusBar, QMenuBar, QToolBar, QTabBar
+from qgis.PyQt.QtWidgets import (
+ QAction,
+ QMainWindow,
+ QApplication,
+ QMenu,
+ QTabWidget,
+ QGridLayout,
+ QSpacerItem,
+ QSizePolicy,
+ QDockWidget,
+ QStatusBar,
+ QMenuBar,
+ QToolBar,
+ QTabBar,
+)
from qgis.PyQt.QtGui import QIcon, QKeySequence
from qgis.gui import QgsMessageBar
-from qgis.core import (
- Qgis,
- QgsApplication,
- QgsSettings,
- QgsMapLayerType
-)
+from qgis.core import Qgis, QgsApplication, QgsSettings, QgsMapLayerType
from qgis.utils import OverrideCursor
from .info_viewer import InfoViewer
@@ -56,8 +65,16 @@ def __init__(self, iface, parent=None):
# restore the window state
settings = QgsSettings()
- self.restoreGeometry(settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray))
- self.restoreState(settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray))
+ self.restoreGeometry(
+ settings.value(
+ "/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray
+ )
+ )
+ self.restoreState(
+ settings.value(
+ "/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray
+ )
+ )
self.toolBar.setIconSize(self.iface.iconSize())
self.toolBarOrientation()
@@ -102,7 +119,7 @@ def itemChanged(self, item):
def reloadButtons(self):
db = self.tree.currentDatabase()
- if not hasattr(self, '_lastDb'):
+ if not hasattr(self, "_lastDb"):
self._lastDb = db
elif db == self._lastDb:
@@ -131,8 +148,12 @@ def refreshTabs(self):
# enable/disable tabs
self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None)
- self.tabs.setTabEnabled(self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType,
- table.RasterType] and table.geomColumn is not None)
+ self.tabs.setTabEnabled(
+ self.tabs.indexOf(self.preview),
+ table is not None
+ and table.type in [table.VectorType, table.RasterType]
+ and table.geomColumn is not None,
+ )
# show the info tab if the current tab is disabled
if not self.tabs.isTabEnabled(index):
self.tabs.setCurrentWidget(self.info)
@@ -154,8 +175,11 @@ def refreshActionSlot(self):
def importActionSlot(self):
db = self.tree.currentDatabase()
if db is None:
- self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, self.iface.messageTimeout())
+ self.infoBar.pushMessage(
+ self.tr("No database selected or you are not connected to it."),
+ Qgis.MessageLevel.Info,
+ self.iface.messageTimeout(),
+ )
return
outUri = db.uri()
@@ -171,15 +195,20 @@ def importActionSlot(self):
def exportActionSlot(self):
table = self.tree.currentTable()
if table is None:
- self.infoBar.pushMessage(self.tr("Select the table you want export to file."), Qgis.MessageLevel.Info,
- self.iface.messageTimeout())
+ self.infoBar.pushMessage(
+ self.tr("Select the table you want export to file."),
+ Qgis.MessageLevel.Info,
+ self.iface.messageTimeout(),
+ )
return
inLayer = table.toMapLayer()
if inLayer.type() != QgsMapLayerType.VectorLayer:
self.infoBar.pushMessage(
self.tr("Select a vector or a tabular layer you want export."),
- Qgis.MessageLevel.Warning, self.iface.messageTimeout())
+ Qgis.MessageLevel.Warning,
+ self.iface.messageTimeout(),
+ )
return
from .dlg_export_vector import DlgExportVector
@@ -192,8 +221,11 @@ def exportActionSlot(self):
def runSqlWindow(self):
db = self.tree.currentDatabase()
if db is None:
- self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, self.iface.messageTimeout())
+ self.infoBar.pushMessage(
+ self.tr("No database selected or you are not connected to it."),
+ Qgis.MessageLevel.Info,
+ self.iface.messageTimeout(),
+ )
# force displaying of the message, it appears on the first tab (i.e. Info)
self.tabs.setCurrentIndex(0)
return
@@ -206,10 +238,13 @@ def runSqlWindow(self):
index = self.tabs.addTab(query, tabname)
self.tabs.setTabIcon(index, db.connection().icon())
self.tabs.setCurrentIndex(index)
- query.nameChanged.connect(functools.partial(self.update_query_tab_name, index, dbname))
+ query.nameChanged.connect(
+ functools.partial(self.update_query_tab_name, index, dbname)
+ )
def runSqlLayerWindow(self, layer):
from .dlg_sql_layer_window import DlgSqlLayerWindow
+
query = DlgSqlLayerWindow(self.iface, layer, self)
lname = layer.name()
tabname = self.tr("Layer ({0})").format(lname)
@@ -220,18 +255,19 @@ def runSqlLayerWindow(self, layer):
def update_query_tab_name(self, index, dbname, queryname):
if not queryname:
queryname = self.tr("Query")
- tabname = "%s (%s)" % (queryname, dbname)
+ tabname = f"{queryname} ({dbname})"
self.tabs.setTabText(index, tabname)
def showSystemTables(self):
self.tree.showSystemTables(self.actionShowSystemTables.isChecked())
def registerAction(self, action, menuName, callback=None):
- """ register an action to the manager's main menu """
- if not hasattr(self, '_registeredDbActions'):
+ """register an action to the manager's main menu"""
+ if not hasattr(self, "_registeredDbActions"):
self._registeredDbActions = {}
if callback is not None:
+
def invoke_callback(x):
return self.invokeCallback(callback)
@@ -272,7 +308,9 @@ def invoke_callback(x):
# get the placeholder's position to insert before it
pos = 0
for pos in range(len(menuActions)):
- if menuActions[pos].isSeparator() and menuActions[pos].objectName().endswith("_placeholder"):
+ if menuActions[pos].isSeparator() and menuActions[
+ pos
+ ].objectName().endswith("_placeholder"):
menuActions[pos].setVisible(True)
break
@@ -294,12 +332,12 @@ def invoke_callback(x):
return True
def invokeCallback(self, callback, *params):
- """ Call a method passing the selected item in the database tree,
- the sender (usually a QAction), the plugin mainWindow and
- optionally additional parameters.
+ """Call a method passing the selected item in the database tree,
+ the sender (usually a QAction), the plugin mainWindow and
+ optionally additional parameters.
- This method takes care to override and restore the cursor,
- but also catches exceptions and displays the error dialog.
+ This method takes care to override and restore the cursor,
+ but also catches exceptions and displays the error dialog.
"""
with OverrideCursor(Qt.CursorShape.WaitCursor):
try:
@@ -309,7 +347,7 @@ def invokeCallback(self, callback, *params):
DlgDbError.showError(e, self)
def unregisterAction(self, action, menuName):
- if not hasattr(self, '_registeredDbActions'):
+ if not hasattr(self, "_registeredDbActions"):
return
if menuName is None or menuName == "":
@@ -340,7 +378,9 @@ def unregisterAction(self, action, menuName):
# hide the placeholder if there're no other registered actions
if len(self._registeredDbActions[menuName]) <= 0:
for i in range(len(menuActions)):
- if menuActions[i].isSeparator() and menuActions[i].objectName().endswith("_placeholder"):
+ if menuActions[i].isSeparator() and menuActions[
+ i
+ ].objectName().endswith("_placeholder"):
menuActions[i].setVisible(False)
break
@@ -350,7 +390,7 @@ def unregisterAction(self, action, menuName):
return False
def unregisterAllActions(self):
- if not hasattr(self, '_registeredDbActions'):
+ if not hasattr(self, "_registeredDbActions"):
return
for menuName in self._registeredDbActions:
@@ -400,14 +440,20 @@ def setupUi(self):
self.tabs.tabCloseRequested.connect(self.close_tab)
tabbar = self.tabs.tabBar()
for i in range(3):
- btn = tabbar.tabButton(i, QTabBar.ButtonPosition.RightSide) if tabbar.tabButton(i, QTabBar.ButtonPosition.RightSide) else tabbar.tabButton(i, QTabBar.ButtonPosition.LeftSide)
+ btn = (
+ tabbar.tabButton(i, QTabBar.ButtonPosition.RightSide)
+ if tabbar.tabButton(i, QTabBar.ButtonPosition.RightSide)
+ else tabbar.tabButton(i, QTabBar.ButtonPosition.LeftSide)
+ )
btn.resize(0, 0)
btn.hide()
# Creates layout for message bar
self.layout = QGridLayout(self.info)
self.layout.setContentsMargins(0, 0, 0, 0)
- spacerItem = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
+ spacerItem = QSpacerItem(
+ 20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
+ )
self.layout.addItem(spacerItem, 1, 0, 1, 1)
# init messageBar instance
self.infoBar = QgsMessageBar(self.info)
@@ -452,15 +498,18 @@ def setupUi(self):
sep.setObjectName("DB_Manager_DbMenu_placeholder")
sep.setVisible(False)
- self.actionRefresh = QAction(QgsApplication.getThemeIcon("/mActionRefresh.svg"), self.tr("&Refresh"),
- self.menuDb)
+ self.actionRefresh = QAction(
+ QgsApplication.getThemeIcon("/mActionRefresh.svg"),
+ self.tr("&Refresh"),
+ self.menuDb,
+ )
self.actionRefresh.triggered.connect(self.refreshActionSlot)
self.actionRefresh.setShortcut(QKeySequence("F5"))
self.menuDb.addAction(self.actionRefresh)
- self.actionSqlWindow = QAction(GuiUtils.get_icon('mActionSQLWindow'),
- self.tr("&SQL Window"),
- self.menuDb)
+ self.actionSqlWindow = QAction(
+ GuiUtils.get_icon("mActionSQLWindow"), self.tr("&SQL Window"), self.menuDb
+ )
self.actionSqlWindow.triggered.connect(self.runSqlWindow)
self.actionSqlWindow.setShortcut(QKeySequence("F2"))
self.menuDb.addAction(self.actionSqlWindow)
@@ -484,12 +533,16 @@ def setupUi(self):
sep.setObjectName("DB_Manager_TableMenu_placeholder")
sep.setVisible(False)
- self.actionImport = self.menuTable.addAction(GuiUtils.get_icon("mActionDBImport"),
- QApplication.translate("DBManager", "&Import Layer/File…"),
- self.importActionSlot)
- self.actionExport = self.menuTable.addAction(GuiUtils.get_icon("mActionDBExport"),
- QApplication.translate("DBManager", "&Export to File…"),
- self.exportActionSlot)
+ self.actionImport = self.menuTable.addAction(
+ GuiUtils.get_icon("mActionDBImport"),
+ QApplication.translate("DBManager", "&Import Layer/File…"),
+ self.importActionSlot,
+ )
+ self.actionExport = self.menuTable.addAction(
+ GuiUtils.get_icon("mActionDBExport"),
+ QApplication.translate("DBManager", "&Export to File…"),
+ self.exportActionSlot,
+ )
self.menuTable.addSeparator()
# self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables)
# self.actionShowSystemTables.setCheckable(True)
diff --git a/python/plugins/db_manager/db_manager_plugin.py b/python/plugins/db_manager/db_manager_plugin.py
index cd0ea9318e05..78d996c5a867 100644
--- a/python/plugins/db_manager/db_manager_plugin.py
+++ b/python/plugins/db_manager/db_manager_plugin.py
@@ -22,12 +22,7 @@
from qgis.PyQt.QtWidgets import QAction, QApplication
from qgis.PyQt.QtGui import QIcon
-from qgis.core import (
- QgsProject,
- QgsMapLayerType,
- QgsDataSourceUri,
- QgsApplication
-)
+from qgis.core import QgsProject, QgsMapLayerType, QgsDataSourceUri, QgsApplication
class DBManagerPlugin:
@@ -37,28 +32,37 @@ def __init__(self, iface):
self.dlg = None
def initGui(self):
- self.action = QAction(QgsApplication.getThemeIcon('dbmanager.svg'), QApplication.translate("DBManagerPlugin", "DB Manager…"),
- self.iface.mainWindow())
+ self.action = QAction(
+ QgsApplication.getThemeIcon("dbmanager.svg"),
+ QApplication.translate("DBManagerPlugin", "DB Manager…"),
+ self.iface.mainWindow(),
+ )
self.action.setObjectName("dbManager")
self.action.triggered.connect(self.run)
# Add toolbar button and menu item
- if hasattr(self.iface, 'addDatabaseToolBarIcon'):
+ if hasattr(self.iface, "addDatabaseToolBarIcon"):
self.iface.addDatabaseToolBarIcon(self.action)
else:
self.iface.addToolBarIcon(self.action)
- if hasattr(self.iface, 'addPluginToDatabaseMenu'):
- self.iface.addPluginToDatabaseMenu(QApplication.translate("DBManagerPlugin", None), self.action)
+ if hasattr(self.iface, "addPluginToDatabaseMenu"):
+ self.iface.addPluginToDatabaseMenu(
+ QApplication.translate("DBManagerPlugin", None), self.action
+ )
else:
- self.iface.addPluginToMenu(QApplication.translate("DBManagerPlugin", "DB Manager"), self.action)
+ self.iface.addPluginToMenu(
+ QApplication.translate("DBManagerPlugin", "DB Manager"), self.action
+ )
def unload(self):
# Remove the plugin menu item and icon
- if hasattr(self.iface, 'databaseMenu'):
+ if hasattr(self.iface, "databaseMenu"):
self.iface.databaseMenu().removeAction(self.action)
else:
- self.iface.removePluginMenu(QApplication.translate("DBManagerPlugin", "DB Manager"), self.action)
- if hasattr(self.iface, 'removeDatabaseToolBarIcon'):
+ self.iface.removePluginMenu(
+ QApplication.translate("DBManagerPlugin", "DB Manager"), self.action
+ )
+ if hasattr(self.iface, "removeDatabaseToolBarIcon"):
self.iface.removeDatabaseToolBarIcon(self.action)
else:
self.iface.removeToolBarIcon(self.action)
@@ -69,7 +73,7 @@ def unload(self):
def onUpdateSqlLayer(self):
# Be able to update every Db layer from Postgres, Spatialite and Oracle
l = self.iface.activeLayer()
- if l.dataProvider().name() in ['postgres', 'spatialite', 'oracle']:
+ if l.dataProvider().name() in ["postgres", "spatialite", "oracle"]:
self.run()
self.dlg.runSqlLayerWindow(l)
# virtual has QUrl source
@@ -87,7 +91,9 @@ def run(self):
self.dlg.destroyed.connect(self.onDestroyed)
self.dlg.show()
self.dlg.raise_()
- self.dlg.setWindowState(self.dlg.windowState() & ~Qt.WindowState.WindowMinimized)
+ self.dlg.setWindowState(
+ self.dlg.windowState() & ~Qt.WindowState.WindowMinimized
+ )
self.dlg.activateWindow()
def onDestroyed(self, obj):
diff --git a/python/plugins/db_manager/db_model.py b/python/plugins/db_manager/db_model.py
index 14d30472bfd3..191aaed1e40d 100644
--- a/python/plugins/db_manager/db_model.py
+++ b/python/plugins/db_manager/db_model.py
@@ -19,7 +19,19 @@
"""
from functools import partial
-from qgis.PyQt.QtCore import Qt, QObject, qDebug, QByteArray, QMimeData, QDataStream, QIODevice, QFileInfo, QAbstractItemModel, QModelIndex, pyqtSignal
+from qgis.PyQt.QtCore import (
+ Qt,
+ QObject,
+ qDebug,
+ QByteArray,
+ QMimeData,
+ QDataStream,
+ QIODevice,
+ QFileInfo,
+ QAbstractItemModel,
+ QModelIndex,
+ pyqtSignal,
+)
from qgis.PyQt.QtWidgets import QApplication, QMessageBox
from qgis.PyQt.QtGui import QIcon
@@ -156,7 +168,7 @@ def __init__(self, connection, parent=None):
connection.deleted.connect(self.itemDeleted)
# load (shared) icon with first instance of table item
- if not hasattr(ConnectionItem, 'connectedIcon'):
+ if not hasattr(ConnectionItem, "connectedIcon"):
ConnectionItem.connectedIcon = GuiUtils.get_icon("plugged")
ConnectionItem.disconnectedIcon = GuiUtils.get_icon("unplugged")
@@ -214,7 +226,7 @@ def __init__(self, schema, parent):
schema.deleted.connect(self.itemDeleted)
# load (shared) icon with first instance of schema item
- if not hasattr(SchemaItem, 'schemaIcon'):
+ if not hasattr(SchemaItem, "schemaIcon"):
SchemaItem.schemaIcon = GuiUtils.get_icon("namespace")
def data(self, column):
@@ -245,14 +257,20 @@ def __init__(self, table, parent):
self.populate()
# load (shared) icon with first instance of table item
- if not hasattr(TableItem, 'tableIcon'):
+ if not hasattr(TableItem, "tableIcon"):
TableItem.tableIcon = QgsApplication.getThemeIcon("/mIconTableLayer.svg")
TableItem.viewIcon = GuiUtils.get_icon("view")
TableItem.viewMaterializedIcon = GuiUtils.get_icon("view_materialized")
- TableItem.layerPointIcon = QgsApplication.getThemeIcon("/mIconPointLayer.svg")
+ TableItem.layerPointIcon = QgsApplication.getThemeIcon(
+ "/mIconPointLayer.svg"
+ )
TableItem.layerLineIcon = QgsApplication.getThemeIcon("/mIconLineLayer.svg")
- TableItem.layerPolygonIcon = QgsApplication.getThemeIcon("/mIconPolygonLayer.svg")
- TableItem.layerRasterIcon = QgsApplication.getThemeIcon("/mIconRasterLayer.svg")
+ TableItem.layerPolygonIcon = QgsApplication.getThemeIcon(
+ "/mIconPolygonLayer.svg"
+ )
+ TableItem.layerRasterIcon = QgsApplication.getThemeIcon(
+ "/mIconRasterLayer.svg"
+ )
TableItem.layerUnknownIcon = GuiUtils.get_icon("layer_unknown")
def data(self, column):
@@ -267,11 +285,15 @@ def icon(self):
if self.getItemData().type == Table.VectorType:
geom_type = self.getItemData().geomType
if geom_type is not None:
- if geom_type.find('POINT') != -1:
+ if geom_type.find("POINT") != -1:
return self.layerPointIcon
- elif geom_type.find('LINESTRING') != -1 or geom_type in ('CIRCULARSTRING', 'COMPOUNDCURVE', 'MULTICURVE'):
+ elif geom_type.find("LINESTRING") != -1 or geom_type in (
+ "CIRCULARSTRING",
+ "COMPOUNDCURVE",
+ "MULTICURVE",
+ ):
return self.layerLineIcon
- elif geom_type.find('POLYGON') != -1 or geom_type == 'MULTISURFACE':
+ elif geom_type.find("POLYGON") != -1 or geom_type == "MULTISURFACE":
return self.layerPolygonIcon
return self.layerUnknownIcon
@@ -279,7 +301,10 @@ def icon(self):
return self.layerRasterIcon
if self.getItemData().isView:
- if hasattr(self.getItemData(), '_relationType') and self.getItemData()._relationType == 'm':
+ if (
+ hasattr(self.getItemData(), "_relationType")
+ and self.getItemData()._relationType == "m"
+ ):
return self.viewMaterializedIcon
else:
return self.viewIcon
@@ -291,7 +316,7 @@ def path(self):
pathList.extend(self.parent().path())
if self.getItemData().type == Table.VectorType:
- pathList.append("%s::%s" % (self.data(0), self.getItemData().geomColumn))
+ pathList.append(f"{self.data(0)}::{self.getItemData().geomColumn}")
else:
pathList.append(self.data(0))
@@ -307,7 +332,7 @@ def __init__(self, parent=None):
QAbstractItemModel.__init__(self, parent)
self.treeView = parent
- self.header = [self.tr('Databases')]
+ self.header = [self.tr("Databases")]
if isImportVectorAvail:
self.importVector.connect(self.vectorImport)
@@ -400,10 +425,14 @@ def flags(self, index):
if index.column() == 0:
item = index.internalPointer()
- if isinstance(item, SchemaItem) \
- or (isinstance(item, TableItem)
- and not (self.hasGPKGSupport and item.getItemData().type == Table.RasterType
- and int(gdal.VersionInfo()) < 3100000)):
+ if isinstance(item, SchemaItem) or (
+ isinstance(item, TableItem)
+ and not (
+ self.hasGPKGSupport
+ and item.getItemData().type == Table.RasterType
+ and int(gdal.VersionInfo()) < 3100000
+ )
+ ):
flags |= Qt.ItemFlag.ItemIsEditable
if isinstance(item, TableItem):
@@ -424,7 +453,11 @@ def flags(self, index):
return flags
def headerData(self, section, orientation, role):
- if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole and section < len(self.header):
+ if (
+ orientation == Qt.Orientation.Horizontal
+ and role == Qt.ItemDataRole.DisplayRole
+ and section < len(self.header)
+ ):
return self.header[section]
return None
@@ -546,9 +579,17 @@ def dropMimeData(self, data, action, row, column, parent):
return True
# vectors/tables to be imported must be dropped on connected db, schema or table
- canImportLayer = isImportVectorAvail and parent.isValid() and \
- (isinstance(parent.internalPointer(), (SchemaItem, TableItem)) or
- (isinstance(parent.internalPointer(), ConnectionItem) and parent.internalPointer().populated))
+ canImportLayer = (
+ isImportVectorAvail
+ and parent.isValid()
+ and (
+ isinstance(parent.internalPointer(), (SchemaItem, TableItem))
+ or (
+ isinstance(parent.internalPointer(), ConnectionItem)
+ and parent.internalPointer().populated
+ )
+ )
+ )
added = 0
@@ -578,20 +619,24 @@ def dropMimeData(self, data, action, row, column, parent):
if canImportLayer:
if QgsRasterLayer.isValidRasterFileName(filename):
- layerType = 'raster'
- providerKey = 'gdal'
+ layerType = "raster"
+ providerKey = "gdal"
else:
- layerType = 'vector'
- providerKey = 'ogr'
+ layerType = "vector"
+ providerKey = "ogr"
layerName = QFileInfo(filename).completeBaseName()
- if self.importLayer(layerType, providerKey, layerName, filename, parent):
+ if self.importLayer(
+ layerType, providerKey, layerName, filename, parent
+ ):
added += 1
if data.hasFormat(self.QGIS_URI_MIME):
for uri in QgsMimeDataUtils.decodeUriList(data):
if canImportLayer:
- if self.importLayer(uri.layerType, uri.providerKey, uri.name, uri.uri, parent):
+ if self.importLayer(
+ uri.layerType, uri.providerKey, uri.name, uri.uri, parent
+ ):
added += 1
return added > 0
@@ -602,7 +647,7 @@ def importLayer(self, layerType, providerKey, layerName, uriString, parent):
if not isImportVectorAvail:
return False
- if layerType == 'raster':
+ if layerType == "raster":
return False # not implemented yet
inLayer = QgsRasterLayer(uriString, layerName, providerKey)
else:
@@ -610,7 +655,11 @@ def importLayer(self, layerType, providerKey, layerName, uriString, parent):
if not inLayer.isValid():
# invalid layer
- QMessageBox.warning(None, self.tr("Invalid layer"), self.tr("Unable to load the layer {0}").format(inLayer.name()))
+ QMessageBox.warning(
+ None,
+ self.tr("Invalid layer"),
+ self.tr("Unable to load the layer {0}").format(inLayer.name()),
+ )
return False
# retrieve information about the new table's db and schema
@@ -630,11 +679,15 @@ def importLayer(self, layerType, providerKey, layerName, uriString, parent):
if inLayer.type() == inLayer.VectorLayer:
# create the output uri
- schema = outSchema.name if outDb.schemas() is not None and outSchema is not None else ""
+ schema = (
+ outSchema.name
+ if outDb.schemas() is not None and outSchema is not None
+ else ""
+ )
pkCol = geomCol = ""
# default pk and geom field name value
- if providerKey in ['postgres', 'spatialite']:
+ if providerKey in ["postgres", "spatialite"]:
inUri = QgsDataSourceUri(inLayer.source())
pkCol = inUri.keyColumn()
geomCol = inUri.geometryColumn()
diff --git a/python/plugins/db_manager/db_plugins/__init__.py b/python/plugins/db_manager/db_plugins/__init__.py
index 41f61c7bb5c9..e35b113dcf9c 100644
--- a/python/plugins/db_manager/db_plugins/__init__.py
+++ b/python/plugins/db_manager/db_plugins/__init__.py
@@ -23,11 +23,14 @@ class NotSupportedDbType(Exception):
def __init__(self, dbtype):
from qgis.PyQt.QtWidgets import QApplication
- self.msg = QApplication.translate("DBManagerPlugin", "{0} is not supported yet").format(dbtype)
+
+ self.msg = QApplication.translate(
+ "DBManagerPlugin", "{0} is not supported yet"
+ ).format(dbtype)
Exception(self, self.msg)
def __str__(self):
- return self.msg.encode('utf-8')
+ return self.msg.encode("utf-8")
def initDbPluginList():
@@ -35,7 +38,7 @@ def initDbPluginList():
current_dir = os.path.dirname(__file__)
for name in os.listdir(current_dir):
- if name == '__pycache__':
+ if name == "__pycache__":
continue
if not os.path.isdir(os.path.join(current_dir, name)):
continue
@@ -43,7 +46,7 @@ def initDbPluginList():
try:
exec("from .%s import plugin as mod" % name, globals())
except ImportError as e:
- DBPLUGIN_ERRORS.append("%s: %s" % (name, str(e)))
+ DBPLUGIN_ERRORS.append(f"{name}: {str(e)}")
continue
pluginclass = mod.classFactory() # NOQA
diff --git a/python/plugins/db_manager/db_plugins/connector.py b/python/plugins/db_manager/db_plugins/connector.py
index 4cda39e1992b..49ec950e57ba 100644
--- a/python/plugins/db_manager/db_plugins/connector.py
+++ b/python/plugins/db_manager/db_plugins/connector.py
@@ -26,8 +26,7 @@
class DBConnector:
def __init__(self, uri):
- """Creates a new DB connector
- """
+ """Creates a new DB connector"""
self.connection = None
self._uri = uri
@@ -95,17 +94,22 @@ def _execute(self, cursor, sql):
return cursor
def _execute_and_commit(self, sql):
- """ tries to execute and commit some action, on error it rolls back the change """
+ """tries to execute and commit some action, on error it rolls back the change"""
self._execute(None, sql)
self._commit()
def _get_cursor(self, name=None):
try:
if name is not None:
- name = str(name).encode('ascii', 'replace').replace('?', "_")
- self._last_cursor_named_id = 0 if not hasattr(self,
- '_last_cursor_named_id') else self._last_cursor_named_id + 1
- return self.connection.cursor("%s_%d" % (name, self._last_cursor_named_id))
+ name = str(name).encode("ascii", "replace").replace("?", "_")
+ self._last_cursor_named_id = (
+ 0
+ if not hasattr(self, "_last_cursor_named_id")
+ else self._last_cursor_named_id + 1
+ )
+ return self.connection.cursor(
+ "%s_%d" % (name, self._last_cursor_named_id)
+ )
return self.connection.cursor()
@@ -183,36 +187,33 @@ def _get_cursor_columns(self, c):
@classmethod
def quoteId(self, identifier):
- if hasattr(identifier, '__iter__') and not isinstance(identifier, str):
- return '.'.join(
- self.quoteId(i)
- for i in identifier
- if i is not None and i != ""
+ if hasattr(identifier, "__iter__") and not isinstance(identifier, str):
+ return ".".join(
+ self.quoteId(i) for i in identifier if i is not None and i != ""
)
- identifier = str(
- identifier) if identifier is not None else '' # make sure it's python unicode string
+ identifier = (
+ str(identifier) if identifier is not None else ""
+ ) # make sure it's python unicode string
return '"%s"' % identifier.replace('"', '""')
@classmethod
def quoteString(self, txt):
- """ make the string safe - replace ' with '' """
- if hasattr(txt, '__iter__') and not isinstance(txt, str):
- return '.'.join(
- self.quoteString(i)
- for i in txt
- if i is not None
- )
+ """make the string safe - replace ' with ''"""
+ if hasattr(txt, "__iter__") and not isinstance(txt, str):
+ return ".".join(self.quoteString(i) for i in txt if i is not None)
- txt = str(txt) if txt is not None else '' # make sure it's python unicode string
+ txt = (
+ str(txt) if txt is not None else ""
+ ) # make sure it's python unicode string
return "'%s'" % txt.replace("'", "''")
@classmethod
def getSchemaTableName(self, table):
- if not hasattr(table, '__iter__') and not isinstance(table, str):
+ if not hasattr(table, "__iter__") and not isinstance(table, str):
return (None, table)
if isinstance(table, str):
- table = table.split('.')
+ table = table.split(".")
if len(table) < 2:
return (None, table[0])
else:
@@ -220,7 +221,7 @@ def getSchemaTableName(self, table):
@classmethod
def getSqlDictionary(self):
- """ return a generic SQL dictionary """
+ """return a generic SQL dictionary"""
try:
from ..sql_dictionary import getSqlDictionary
@@ -230,7 +231,7 @@ def getSqlDictionary(self):
def getComment(self, tablename, field):
"""Returns the comment for a field"""
- return ''
+ return ""
def commentTable(self, schema, tablename, comment=None):
"""Comment the table"""
diff --git a/python/plugins/db_manager/db_plugins/data_model.py b/python/plugins/db_manager/db_plugins/data_model.py
index 1087035fe8ba..a8e9b9ae918f 100644
--- a/python/plugins/db_manager/db_plugins/data_model.py
+++ b/python/plugins/db_manager/db_plugins/data_model.py
@@ -18,15 +18,15 @@
***************************************************************************/
"""
-from qgis.PyQt.QtCore import (Qt,
- QElapsedTimer,
- QRegularExpression,
- QAbstractTableModel,
- pyqtSignal,
- QObject)
-from qgis.PyQt.QtGui import (QFont,
- QStandardItemModel,
- QStandardItem)
+from qgis.PyQt.QtCore import (
+ Qt,
+ QElapsedTimer,
+ QRegularExpression,
+ QAbstractTableModel,
+ pyqtSignal,
+ QObject,
+)
+from qgis.PyQt.QtGui import QFont, QStandardItemModel, QStandardItem
from qgis.PyQt.QtWidgets import QApplication
from qgis.core import QgsTask
@@ -47,8 +47,7 @@ def headerToString(self, sep="\t"):
def rowToString(self, row, sep="\t"):
return sep.join(
- str(self.getData(row, col))
- for col in range(self.columnCount())
+ str(self.getData(row, col)) for col in range(self.columnCount())
)
def getData(self, row, col):
@@ -64,9 +63,11 @@ def columnCount(self, parent=None):
return len(self._header)
def data(self, index, role):
- if role not in [Qt.ItemDataRole.DisplayRole,
- Qt.ItemDataRole.EditRole,
- Qt.ItemDataRole.FontRole]:
+ if role not in [
+ Qt.ItemDataRole.DisplayRole,
+ Qt.ItemDataRole.EditRole,
+ Qt.ItemDataRole.FontRole,
+ ]:
return None
val = self.getData(index.row(), index.column())
@@ -92,7 +93,9 @@ def data(self, index, role):
try:
return str(val) # convert to Unicode
except UnicodeDecodeError:
- return str(val, 'utf-8', 'replace') # convert from utf8 and replace errors (if any)
+ return str(
+ val, "utf-8", "replace"
+ ) # convert from utf8 and replace errors (if any)
def headerData(self, section, orientation, role):
if role != Qt.ItemDataRole.DisplayRole:
@@ -121,16 +124,22 @@ def __init__(self, table, parent=None):
self.fields.append(self._sanitizeTableField(fld))
self.fetchedCount = 201
- self.fetchedFrom = -self.fetchedCount - 1 # so the first call to getData will exec fetchMoreData(0)
+ self.fetchedFrom = (
+ -self.fetchedCount - 1
+ ) # so the first call to getData will exec fetchMoreData(0)
def _sanitizeTableField(self, field):
- """ quote column names to avoid some problems (e.g. columns with upper case) """
+ """quote column names to avoid some problems (e.g. columns with upper case)"""
return self.db.quoteId(field)
def getData(self, row, col):
if row < self.fetchedFrom or row >= self.fetchedFrom + self.fetchedCount:
margin = self.fetchedCount / 2
- start = int(self.rowCount() - margin if row + margin >= self.rowCount() else row - margin)
+ start = int(
+ self.rowCount() - margin
+ if row + margin >= self.rowCount()
+ else row - margin
+ )
if start < 0:
start = 0
self.fetchMoreData(start)
@@ -141,7 +150,11 @@ def fetchMoreData(self, row_start):
def rowCount(self, index=None):
# case for tables with no columns ... any reason to use them? :-)
- return self.table.rowCount if self.table.rowCount is not None and self.columnCount(index) > 0 else 0
+ return (
+ self.table.rowCount
+ if self.table.rowCount is not None and self.columnCount(index) > 0
+ else 0
+ )
class SqlResultModelAsync(QObject):
@@ -149,7 +162,7 @@ class SqlResultModelAsync(QObject):
def __init__(self):
super().__init__()
- self.error = BaseError('')
+ self.error = BaseError("")
self.status = None
self.model = None
self.task = None
@@ -172,11 +185,13 @@ def modelDone(self):
class SqlResultModelTask(QgsTask):
def __init__(self, db, sql, parent):
- super().__init__(description=QApplication.translate("DBManagerPlugin", "Executing SQL"))
+ super().__init__(
+ description=QApplication.translate("DBManagerPlugin", "Executing SQL")
+ )
self.db = db
self.sql = sql
self.parent = parent
- self.error = BaseError('')
+ self.error = BaseError("")
self.model = None
@@ -231,12 +246,19 @@ def rowFromData(self, data):
row = []
for c in data:
item = QStandardItem(str(c))
- item.setFlags((item.flags() | Qt.ItemFlag.ItemIsEditable) if self.editable else (item.flags() & ~Qt.ItemFlag.ItemIsEditable))
+ item.setFlags(
+ (item.flags() | Qt.ItemFlag.ItemIsEditable)
+ if self.editable
+ else (item.flags() & ~Qt.ItemFlag.ItemIsEditable)
+ )
row.append(item)
return row
def headerData(self, section, orientation, role):
- if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
+ if (
+ orientation == Qt.Orientation.Horizontal
+ and role == Qt.ItemDataRole.DisplayRole
+ ):
return self.header[section]
return None
@@ -254,27 +276,46 @@ def getObjectIter(self):
class TableFieldsModel(SimpleTableModel):
def __init__(self, parent, editable=False):
- SimpleTableModel.__init__(self, ['Name', 'Type', 'Null', 'Default', 'Comment'], editable, parent)
+ SimpleTableModel.__init__(
+ self, ["Name", "Type", "Null", "Default", "Comment"], editable, parent
+ )
def headerData(self, section, orientation, role):
- if orientation == Qt.Orientation.Vertical and role == Qt.ItemDataRole.DisplayRole:
+ if (
+ orientation == Qt.Orientation.Vertical
+ and role == Qt.ItemDataRole.DisplayRole
+ ):
return section + 1
return SimpleTableModel.headerData(self, section, orientation, role)
def flags(self, index):
flags = SimpleTableModel.flags(self, index)
- if index.column() == 2 and flags & Qt.ItemFlag.ItemIsEditable: # set Null column as checkable instead of editable
- flags = flags & ~Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsUserCheckable
+ if (
+ index.column() == 2 and flags & Qt.ItemFlag.ItemIsEditable
+ ): # set Null column as checkable instead of editable
+ flags = (
+ flags & ~Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsUserCheckable
+ )
return flags
def append(self, fld):
- data = [fld.name, fld.type2String(), not fld.notNull, fld.default2String(), fld.getComment()]
+ data = [
+ fld.name,
+ fld.type2String(),
+ not fld.notNull,
+ fld.default2String(),
+ fld.getComment(),
+ ]
self.appendRow(self.rowFromData(data))
row = self.rowCount() - 1
self.setData(self.index(row, 0), fld, Qt.ItemDataRole.UserRole)
self.setData(self.index(row, 1), fld.primaryKey, Qt.ItemDataRole.UserRole)
self.setData(self.index(row, 2), None, Qt.ItemDataRole.DisplayRole)
- self.setData(self.index(row, 2), Qt.CheckState.Unchecked if fld.notNull else Qt.CheckState.Checked, Qt.ItemDataRole.CheckStateRole)
+ self.setData(
+ self.index(row, 2),
+ Qt.CheckState.Unchecked if fld.notNull else Qt.CheckState.Checked,
+ Qt.ItemDataRole.CheckStateRole,
+ )
def _getNewObject(self):
from .plugin import TableField
@@ -295,24 +336,31 @@ def getObject(self, row):
fld.modifier = None
fld.dataType = typestr
- fld.notNull = self.data(self.index(row, 2), Qt.ItemDataRole.CheckStateRole) == Qt.CheckState.Unchecked
+ fld.notNull = (
+ self.data(self.index(row, 2), Qt.ItemDataRole.CheckStateRole)
+ == Qt.CheckState.Unchecked
+ )
fld.primaryKey = self.data(self.index(row, 1), Qt.ItemDataRole.UserRole)
fld.comment = self.data(self.index(row, 4))
return fld
def getFields(self):
- return [
- fld
- for fld in self.getObjectIter()
- ]
+ return [fld for fld in self.getObjectIter()]
class TableConstraintsModel(SimpleTableModel):
def __init__(self, parent, editable=False):
- SimpleTableModel.__init__(self, [QApplication.translate("DBManagerPlugin", 'Name'),
- QApplication.translate("DBManagerPlugin", 'Type'),
- QApplication.translate("DBManagerPlugin", 'Column(s)')], editable, parent)
+ SimpleTableModel.__init__(
+ self,
+ [
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Column(s)"),
+ ],
+ editable,
+ parent,
+ )
def append(self, constr):
field_names = [str(k_v[1].name) for k_v in iter(list(constr.fields().items()))]
@@ -338,17 +386,21 @@ def getObject(self, row):
return constr
def getConstraints(self):
- return [
- constr
- for constr in self.getObjectIter()
- ]
+ return [constr for constr in self.getObjectIter()]
class TableIndexesModel(SimpleTableModel):
def __init__(self, parent, editable=False):
- SimpleTableModel.__init__(self, [QApplication.translate("DBManagerPlugin", 'Name'),
- QApplication.translate("DBManagerPlugin", 'Column(s)')], editable, parent)
+ SimpleTableModel.__init__(
+ self,
+ [
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Column(s)"),
+ ],
+ editable,
+ parent,
+ )
def append(self, idx):
field_names = [str(k_v1[1].name) for k_v1 in iter(list(idx.fields().items()))]
@@ -372,7 +424,4 @@ def getObject(self, row):
return idx
def getIndexes(self):
- return [
- idx
- for idx in self.getObjectIter()
- ]
+ return [idx for idx in self.getObjectIter()]
diff --git a/python/plugins/db_manager/db_plugins/gpkg/connector.py b/python/plugins/db_manager/db_plugins/gpkg/connector.py
index 77de9a3f7606..a7091a0f6ed7 100644
--- a/python/plugins/db_manager/db_plugins/gpkg/connector.py
+++ b/python/plugins/db_manager/db_plugins/gpkg/connector.py
@@ -39,6 +39,7 @@
import sqlite3
from osgeo import gdal, ogr, osr
+
gdal.UseExceptions()
ogr.UseExceptions()
@@ -83,10 +84,24 @@ def _opendb(self):
try:
self.gdal_ds = gdal.OpenEx(self.dbname)
except Exception:
- raise ConnectionError(QApplication.translate("DBManagerPlugin", '"{0}" not found').format(self.dbname))
- if self.gdal_ds.GetDriver().ShortName != 'GPKG':
- raise ConnectionError(QApplication.translate("DBManagerPlugin", '"{dbname}" not recognized as GPKG ({shortname} reported instead.)').format(dbname=self.dbname, shortname=self.gdal_ds.GetDriver().ShortName))
- self.has_raster = self.gdal_ds.RasterCount != 0 or self.gdal_ds.GetMetadata('SUBDATASETS') is not None
+ raise ConnectionError(
+ QApplication.translate("DBManagerPlugin", '"{0}" not found').format(
+ self.dbname
+ )
+ )
+ if self.gdal_ds.GetDriver().ShortName != "GPKG":
+ raise ConnectionError(
+ QApplication.translate(
+ "DBManagerPlugin",
+ '"{dbname}" not recognized as GPKG ({shortname} reported instead.)',
+ ).format(
+ dbname=self.dbname, shortname=self.gdal_ds.GetDriver().ShortName
+ )
+ )
+ self.has_raster = (
+ self.gdal_ds.RasterCount != 0
+ or self.gdal_ds.GetMetadata("SUBDATASETS") is not None
+ )
self.connection = None
self._current_thread = None
@@ -97,7 +112,9 @@ def connection(self):
invalidates it and create a new one.
"""
- if self._connection is None or self._current_thread != int(QThread.currentThreadId()):
+ if self._connection is None or self._current_thread != int(
+ QThread.currentThreadId()
+ ):
self._current_thread = int(QThread.currentThreadId())
try:
self._connection = spatialite_connect(str(self.dbname))
@@ -110,9 +127,13 @@ def connection(self, conn):
self._connection = conn
def unquoteId(self, quotedId):
- if len(quotedId) <= 2 or quotedId[0] != '"' or quotedId[len(quotedId) - 1] != '"':
+ if (
+ len(quotedId) <= 2
+ or quotedId[0] != '"'
+ or quotedId[len(quotedId) - 1] != '"'
+ ):
return quotedId
- unquoted = ''
+ unquoted = ""
i = 1
while i < len(quotedId) - 1:
if quotedId[i] == '"' and quotedId[i + 1] == '"':
@@ -194,7 +215,7 @@ def isValidDatabase(cls, path):
ds = gdal.OpenEx(path)
except Exception:
return False
- return ds.GetDriver().ShortName == 'GPKG'
+ return ds.GetDriver().ShortName == "GPKG"
def getInfo(self):
return None
@@ -217,10 +238,9 @@ def canAddGeometryColumn(self, table):
def canAddSpatialIndex(self, table):
_, tablename = self.getSchemaTableName(table)
lyr = self.gdal_ds.GetLayerByName(tablename)
- if lyr is None or lyr.GetGeometryColumn() == '':
+ if lyr is None or lyr.GetGeometryColumn() == "":
return False
- return not self.hasSpatialIndex(table,
- lyr.GetGeometryColumn())
+ return not self.hasSpatialIndex(table, lyr.GetGeometryColumn())
def hasRasterSupport(self):
return self.has_raster
@@ -243,8 +263,7 @@ def fieldTypes(self):
"TINYINT",
"SMALLINT",
"DOUBLE",
- "FLOAT"
- "DATE",
+ "FLOAT" "DATE",
"DATETIME",
"BOOLEAN",
]
@@ -253,7 +272,7 @@ def getSchemas(self):
return None
def getTables(self, schema=None, add_sys_tables=False):
- """ get list of tables """
+ """get list of tables"""
items = []
try:
@@ -276,62 +295,73 @@ def getTables(self, schema=None, add_sys_tables=False):
return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
def getVectorTables(self, schema=None):
- """Returns a list of vector table information
- """
+ """Returns a list of vector table information"""
items = []
- for table in self.core_connection.tables(schema, QgsAbstractDatabaseProviderConnection.TableFlag.Vector | QgsAbstractDatabaseProviderConnection.TableFlag.Aspatial):
- if not (table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.Aspatial):
+ for table in self.core_connection.tables(
+ schema,
+ QgsAbstractDatabaseProviderConnection.TableFlag.Vector
+ | QgsAbstractDatabaseProviderConnection.TableFlag.Aspatial,
+ ):
+ if not (
+ table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.Aspatial
+ ):
geom_type = table.geometryColumnTypes()[0]
# Use integer PG code for SRID
srid = geom_type.crs.postgisSrid()
geomtype_flatten = QgsWkbTypes.flatType(geom_type.wkbType)
- geomname = 'GEOMETRY'
+ geomname = "GEOMETRY"
if geomtype_flatten == QgsWkbTypes.Type.Point:
- geomname = 'POINT'
+ geomname = "POINT"
elif geomtype_flatten == QgsWkbTypes.Type.LineString:
- geomname = 'LINESTRING'
+ geomname = "LINESTRING"
elif geomtype_flatten == QgsWkbTypes.Type.Polygon:
- geomname = 'POLYGON'
+ geomname = "POLYGON"
elif geomtype_flatten == QgsWkbTypes.Type.MultiPoint:
- geomname = 'MULTIPOINT'
+ geomname = "MULTIPOINT"
elif geomtype_flatten == QgsWkbTypes.Type.MultiLineString:
- geomname = 'MULTILINESTRING'
+ geomname = "MULTILINESTRING"
elif geomtype_flatten == QgsWkbTypes.Type.MultiPolygon:
- geomname = 'MULTIPOLYGON'
+ geomname = "MULTIPOLYGON"
elif geomtype_flatten == QgsWkbTypes.Type.GeometryCollection:
- geomname = 'GEOMETRYCOLLECTION'
+ geomname = "GEOMETRYCOLLECTION"
elif geomtype_flatten == QgsWkbTypes.Type.CircularString:
- geomname = 'CIRCULARSTRING'
+ geomname = "CIRCULARSTRING"
elif geomtype_flatten == QgsWkbTypes.Type.CompoundCurve:
- geomname = 'COMPOUNDCURVE'
+ geomname = "COMPOUNDCURVE"
elif geomtype_flatten == QgsWkbTypes.Type.CurvePolygon:
- geomname = 'CURVEPOLYGON'
+ geomname = "CURVEPOLYGON"
elif geomtype_flatten == QgsWkbTypes.Type.MultiCurve:
- geomname = 'MULTICURVE'
+ geomname = "MULTICURVE"
elif geomtype_flatten == QgsWkbTypes.Type.MultiSurface:
- geomname = 'MULTISURFACE'
- geomdim = 'XY'
+ geomname = "MULTISURFACE"
+ geomdim = "XY"
if QgsWkbTypes.hasZ(geom_type.wkbType):
- geomdim += 'Z'
+ geomdim += "Z"
if QgsWkbTypes.hasM(geom_type.wkbType):
- geomdim += 'M'
+ geomdim += "M"
item = [
Table.VectorType,
table.tableName(),
- bool(table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.View), # is_view
+ bool(
+ table.flags()
+ & QgsAbstractDatabaseProviderConnection.TableFlag.View
+ ), # is_view
table.tableName(),
table.geometryColumn(),
geomname,
geomdim,
- srid
+ srid,
]
self.mapSridToName[srid] = geom_type.crs.description()
else:
item = [
Table.TableType,
table.tableName(),
- bool(table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.View),
+ bool(
+ table.flags()
+ & QgsAbstractDatabaseProviderConnection.TableFlag.View
+ ),
]
items.append(item)
@@ -339,25 +369,29 @@ def getVectorTables(self, schema=None):
return items
def getRasterTables(self, schema=None):
- """ get list of table with a geometry column
- it returns:
- name (table name)
- type = 'view' (is a view?)
- geometry_column:
- r.table_name (the prefix table name, use this to load the layer)
- r.geometry_column
- srid
+ """get list of table with a geometry column
+ it returns:
+ name (table name)
+ type = 'view' (is a view?)
+ geometry_column:
+ r.table_name (the prefix table name, use this to load the layer)
+ r.geometry_column
+ srid
"""
items = []
- for table in self.core_connection.tables(schema, QgsAbstractDatabaseProviderConnection.TableFlag.Raster):
+ for table in self.core_connection.tables(
+ schema, QgsAbstractDatabaseProviderConnection.TableFlag.Raster
+ ):
geom_type = table.geometryColumnTypes()[0]
# Use integer PG code for SRID
srid = geom_type.crs.postgisSrid()
item = [
Table.RasterType,
table.tableName(),
- bool(table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.View),
+ bool(
+ table.flags() & QgsAbstractDatabaseProviderConnection.TableFlag.View
+ ),
table.tableName(),
table.geometryColumn(),
srid,
@@ -372,7 +406,7 @@ def getTableRowCount(self, table):
return lyr.GetFeatureCount() if lyr is not None else None
def getTableFields(self, table):
- """ return list of columns in table """
+ """return list of columns in table"""
sql = "PRAGMA table_info(%s)" % (self.quoteId(table))
ret = self._fetchAll(sql)
if ret is None:
@@ -380,7 +414,7 @@ def getTableFields(self, table):
return ret
def getTableIndexes(self, table):
- """ get info about table's indexes """
+ """get info about table's indexes"""
sql = "PRAGMA index_list(%s)" % (self.quoteId(table))
indexes = self._fetchAll(sql)
if indexes is None:
@@ -398,10 +432,7 @@ def getTableIndexes(self, table):
sql = "PRAGMA index_info(%s)" % (self.quoteId(name))
idx = [num, name, unique]
- cols = [
- cid
- for seq, cid, cname in self._fetchAll(sql)
- ]
+ cols = [cid for seq, cid, cname in self._fetchAll(sql)]
idx.append(cols)
indexes[i] = idx
@@ -414,7 +445,10 @@ def getTableTriggers(self, table):
_, tablename = self.getSchemaTableName(table)
# Do not list rtree related triggers as we don't want them to be dropped
- sql = "SELECT name, sql FROM sqlite_master WHERE tbl_name = %s AND type = 'trigger'" % (self.quoteString(tablename))
+ sql = (
+ "SELECT name, sql FROM sqlite_master WHERE tbl_name = %s AND type = 'trigger'"
+ % (self.quoteString(tablename))
+ )
if self.isVectorTable(table):
sql += " AND name NOT LIKE 'rtree_%%'"
elif self.isRasterTable(table):
@@ -427,21 +461,23 @@ def getTableTriggers(self, table):
return self._fetchAll(sql)
def deleteTableTrigger(self, trigger, table=None):
- """Deletes trigger """
+ """Deletes trigger"""
sql = "DROP TRIGGER %s" % self.quoteId(trigger)
self._execute_and_commit(sql)
def getTableExtent(self, table, geom, force=False):
- """ find out table extent """
+ """find out table extent"""
_, tablename = self.getSchemaTableName(table)
if self.isRasterTable(table):
- md = self.gdal_ds.GetMetadata('SUBDATASETS')
+ md = self.gdal_ds.GetMetadata("SUBDATASETS")
if md is None or len(md) == 0:
ds = self.gdal_ds
else:
- subdataset_name = 'GPKG:%s:%s' % (self.gdal_ds.GetDescription(), tablename)
+ subdataset_name = "GPKG:{}:{}".format(
+ self.gdal_ds.GetDescription(), tablename
+ )
try:
ds = gdal.Open(subdataset_name)
except Exception:
@@ -466,14 +502,17 @@ def getTableExtent(self, table, geom, force=False):
return (minx, miny, maxx, maxy)
def getViewDefinition(self, view):
- """ returns definition of the view """
+ """returns definition of the view"""
return None
def getSpatialRefInfo(self, srid):
if srid in self.mapSridToName:
return self.mapSridToName[srid]
- sql = "SELECT srs_name FROM gpkg_spatial_ref_sys WHERE srs_id = %s" % self.quoteString(srid)
+ sql = (
+ "SELECT srs_name FROM gpkg_spatial_ref_sys WHERE srs_id = %s"
+ % self.quoteString(srid)
+ )
res = self._fetchOne(sql)
if res is not None and len(res) > 0:
res = res[0]
@@ -488,13 +527,18 @@ def isVectorTable(self, table):
def isRasterTable(self, table):
if self.has_raster and not self.isVectorTable(table):
_, tablename = self.getSchemaTableName(table)
- md = self.gdal_ds.GetMetadata('SUBDATASETS')
+ md = self.gdal_ds.GetMetadata("SUBDATASETS")
if md is None or len(md) == 0:
- sql = "SELECT COUNT(*) FROM gpkg_contents WHERE data_type = 'tiles' AND table_name = %s" % self.quoteString(tablename)
+ sql = (
+ "SELECT COUNT(*) FROM gpkg_contents WHERE data_type = 'tiles' AND table_name = %s"
+ % self.quoteString(tablename)
+ )
ret = self._fetchOne(sql)
return ret != [] and ret[0][0] == 1
else:
- subdataset_name = 'GPKG:%s:%s' % (self.gdal_ds.GetDescription(), tablename)
+ subdataset_name = "GPKG:{}:{}".format(
+ self.gdal_ds.GetDescription(), tablename
+ )
for key in md:
if md[key] == subdataset_name:
return True
@@ -505,37 +549,41 @@ def getOGRFieldTypeFromSQL(self, sql_type):
ogr_type = ogr.OFTString
ogr_subtype = ogr.OFSTNone
width = 0
- if not sql_type.startswith('TEXT ('):
- pos = sql_type.find(' (')
+ if not sql_type.startswith("TEXT ("):
+ pos = sql_type.find(" (")
if pos >= 0:
sql_type = sql_type[0:pos]
- if sql_type == 'BOOLEAN':
+ if sql_type == "BOOLEAN":
ogr_type = ogr.OFTInteger
ogr_subtype = ogr.OFSTBoolean
- elif sql_type in ('TINYINT', 'SMALLINT', 'MEDIUMINT'):
+ elif sql_type in ("TINYINT", "SMALLINT", "MEDIUMINT"):
ogr_type = ogr.OFTInteger
- elif sql_type == 'INTEGER':
+ elif sql_type == "INTEGER":
ogr_type = ogr.OFTInteger64
- elif sql_type == 'FLOAT':
+ elif sql_type == "FLOAT":
ogr_type = ogr.OFTReal
ogr_subtype = ogr.OFSTFloat32
- elif sql_type == 'DOUBLE':
+ elif sql_type == "DOUBLE":
ogr_type = ogr.OFTReal
- elif sql_type == 'DATE':
+ elif sql_type == "DATE":
ogr_type = ogr.OFTDate
- elif sql_type == 'DATETIME':
+ elif sql_type == "DATETIME":
ogr_type = ogr.OFTDateTime
- elif sql_type.startswith('TEXT (') and sql_type.endswith(')'):
- width = int(sql_type[len('TEXT ('):-1])
+ elif sql_type.startswith("TEXT (") and sql_type.endswith(")"):
+ width = int(sql_type[len("TEXT (") : -1])
return (ogr_type, ogr_subtype, width)
def createOGRFieldDefnFromSQL(self, sql_fielddef):
- f_split = sql_fielddef.split(' ')
+ f_split = sql_fielddef.split(" ")
quoted_name = f_split[0]
name = self.unquoteId(quoted_name)
sql_type = f_split[1].upper()
- if len(f_split) >= 3 and f_split[2].startswith('(') and f_split[2].endswith(')'):
- sql_type += ' ' + f_split[2]
+ if (
+ len(f_split) >= 3
+ and f_split[2].startswith("(")
+ and f_split[2].endswith(")")
+ ):
+ sql_type += " " + f_split[2]
f_split = [f for f in f_split[3:]]
else:
f_split = [f for f in f_split[2:]]
@@ -543,16 +591,16 @@ def createOGRFieldDefnFromSQL(self, sql_fielddef):
fld_defn = ogr.FieldDefn(name, ogr_type)
fld_defn.SetSubType(ogr_subtype)
fld_defn.SetWidth(width)
- if len(f_split) >= 2 and f_split[0] == 'NOT' and f_split[1] == 'NULL':
+ if len(f_split) >= 2 and f_split[0] == "NOT" and f_split[1] == "NULL":
fld_defn.SetNullable(False)
f_split = [f for f in f_split[2:]]
elif len(f_split) >= 1:
f_split = [f for f in f_split[1:]]
- if len(f_split) >= 2 and f_split[0] == 'DEFAULT':
+ if len(f_split) >= 2 and f_split[0] == "DEFAULT":
new_default = f_split[1]
- if new_default == '':
+ if new_default == "":
fld_defn.SetDefault(None)
- elif new_default == 'NULL' or ogr_type in (ogr.OFTInteger, ogr.OFTReal):
+ elif new_default == "NULL" or ogr_type in (ogr.OFTInteger, ogr.OFTReal):
fld_defn.SetDefault(new_default)
elif new_default.startswith("'") and new_default.endswith("'"):
fld_defn.SetDefault(new_default)
@@ -562,17 +610,19 @@ def createOGRFieldDefnFromSQL(self, sql_fielddef):
def createTable(self, table, field_defs, pkey):
"""Creates ordinary table
- 'fields' is array containing field definitions
- 'pkey' is the primary key name
+ 'fields' is array containing field definitions
+ 'pkey' is the primary key name
"""
if len(field_defs) == 0:
return False
options = []
if pkey is not None and pkey != "":
- options += ['FID=' + pkey]
+ options += ["FID=" + pkey]
_, tablename = self.getSchemaTableName(table)
- lyr = self.gdal_ds.CreateLayer(tablename, geom_type=ogr.wkbNone, options=options)
+ lyr = self.gdal_ds.CreateLayer(
+ tablename, geom_type=ogr.wkbNone, options=options
+ )
if lyr is None:
return False
for field_def in field_defs:
@@ -585,9 +635,9 @@ def createTable(self, table, field_defs, pkey):
return True
def deleteTable(self, table):
- """Deletes table from the database """
+ """Deletes table from the database"""
if self.isRasterTable(table):
- sql = "DROP TABLE {}".format(self.quoteId(table))
+ sql = f"DROP TABLE {self.quoteId(table)}"
self._execute_and_commit(sql)
return True
@@ -598,7 +648,7 @@ def deleteTable(self, table):
return False
def emptyTable(self, table):
- """Deletes all rows from table """
+ """Deletes all rows from table"""
if self.isRasterTable(table):
return False
@@ -617,11 +667,16 @@ def renameTable(self, table, new_table):
"""
try:
name = table[1] # 0 is schema
- vector_table_names = [t.tableName() for t in self.core_connection.tables('', QgsAbstractDatabaseProviderConnection.TableFlag.Vector)]
+ vector_table_names = [
+ t.tableName()
+ for t in self.core_connection.tables(
+ "", QgsAbstractDatabaseProviderConnection.TableFlag.Vector
+ )
+ ]
if name in vector_table_names:
- self.core_connection.renameVectorTable('', name, new_table)
+ self.core_connection.renameVectorTable("", name, new_table)
else:
- self.core_connection.renameRasterTable('', name, new_table)
+ self.core_connection.renameRasterTable("", name, new_table)
return True
except QgsProviderConnectionException:
return False
@@ -630,11 +685,11 @@ def moveTable(self, table, new_table, new_schema=None):
return self.renameTable(table, new_table)
def runVacuum(self):
- """ run vacuum on the db """
+ """run vacuum on the db"""
self._execute_and_commit("VACUUM")
def addTableColumn(self, table, field_def):
- """Adds a column to table """
+ """Adds a column to table"""
_, tablename = self.getSchemaTableName(table)
lyr = self.gdal_ds.GetLayerByName(tablename)
@@ -644,7 +699,7 @@ def addTableColumn(self, table, field_def):
return lyr.CreateField(fld_defn) == 0
def deleteTableColumn(self, table, column):
- """Deletes column from a table """
+ """Deletes column from a table"""
if self.isGeometryColumn(table, column):
return False
@@ -657,7 +712,16 @@ def deleteTableColumn(self, table, column):
return lyr.DeleteField(idx) == 0
return False
- def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not_null=None, new_default=None, comment=None):
+ def updateTableColumn(
+ self,
+ table,
+ column,
+ new_name,
+ new_data_type=None,
+ new_not_null=None,
+ new_default=None,
+ comment=None,
+ ):
if self.isGeometryColumn(table, column):
return False
@@ -682,15 +746,17 @@ def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not
else:
flag |= ogr.ALTER_TYPE_FLAG
flag |= ogr.ALTER_WIDTH_PRECISION_FLAG
- ogr_type, ogr_subtype, width = self.getOGRFieldTypeFromSQL(new_data_type)
+ ogr_type, ogr_subtype, width = self.getOGRFieldTypeFromSQL(
+ new_data_type
+ )
new_fielddefn = ogr.FieldDefn(new_name, ogr_type)
new_fielddefn.SetSubType(ogr_subtype)
new_fielddefn.SetWidth(width)
if new_default is not None:
flag |= ogr.ALTER_DEFAULT_FLAG
- if new_default == '':
+ if new_default == "":
new_fielddefn.SetDefault(None)
- elif new_default == 'NULL' or ogr_type in (ogr.OFTInteger, ogr.OFTReal):
+ elif new_default == "NULL" or ogr_type in (ogr.OFTInteger, ogr.OFTReal):
new_fielddefn.SetDefault(str(new_default))
elif new_default.startswith("'") and new_default.endswith("'"):
new_fielddefn.SetDefault(str(new_default))
@@ -715,36 +781,38 @@ def isGeometryColumn(self, table, column):
return False
return column == lyr.GetGeometryColumn()
- def addGeometryColumn(self, table, geom_column='geometry', geom_type='POINT', srid=-1, dim=2):
+ def addGeometryColumn(
+ self, table, geom_column="geometry", geom_type="POINT", srid=-1, dim=2
+ ):
_, tablename = self.getSchemaTableName(table)
lyr = self.gdal_ds.GetLayerByName(tablename)
if lyr is None:
return False
ogr_type = ogr.wkbUnknown
- if geom_type == 'POINT':
+ if geom_type == "POINT":
ogr_type = ogr.wkbPoint
- elif geom_type == 'LINESTRING':
+ elif geom_type == "LINESTRING":
ogr_type = ogr.wkbLineString
- elif geom_type == 'POLYGON':
+ elif geom_type == "POLYGON":
ogr_type = ogr.wkbPolygon
- elif geom_type == 'MULTIPOINT':
+ elif geom_type == "MULTIPOINT":
ogr_type = ogr.wkbMultiPoint
- elif geom_type == 'MULTILINESTRING':
+ elif geom_type == "MULTILINESTRING":
ogr_type = ogr.wkbMultiLineString
- elif geom_type == 'MULTIPOLYGON':
+ elif geom_type == "MULTIPOLYGON":
ogr_type = ogr.wkbMultiPolygon
- elif geom_type == 'GEOMETRYCOLLECTION':
+ elif geom_type == "GEOMETRYCOLLECTION":
ogr_type = ogr.wkbGeometryCollection
if dim == 3:
ogr_type = ogr_type | ogr.wkb25DBit
elif dim == 4:
- if hasattr(ogr, 'GT_HasZ'):
+ if hasattr(ogr, "GT_HasZ"):
ogr_type = ogr.GT_SetZ(ogr_type)
else:
ogr_type = ogr_type | ogr.wkb25DBit
- if hasattr(ogr, 'GT_HasM'):
+ if hasattr(ogr, "GT_HasM"):
ogr_type = ogr.GT_SetM(ogr_type)
geom_field_defn = ogr.GeomFieldDefn(self.unquoteId(geom_column), ogr_type)
@@ -762,23 +830,26 @@ def deleteGeometryColumn(self, table, geom_column):
return False # not supported
def addTableUniqueConstraint(self, table, column):
- """Adds a unique constraint to a table """
+ """Adds a unique constraint to a table"""
return False # constraints not supported
def deleteTableConstraint(self, table, constraint):
- """Deletes constraint in a table """
+ """Deletes constraint in a table"""
return False # constraints not supported
def addTablePrimaryKey(self, table, column):
- """Adds a primery key (with one column) to a table """
- sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
+ """Adds a primery key (with one column) to a table"""
+ sql = "ALTER TABLE {} ADD PRIMARY KEY ({})".format(
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def createTableIndex(self, table, name, column, unique=False):
- """Creates index on one column using default options """
+ """Creates index on one column using default options"""
unique_str = "UNIQUE" if unique else ""
- sql = "CREATE %s INDEX %s ON %s (%s)" % (
- unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column))
+ sql = "CREATE {} INDEX {} ON {} ({})".format(
+ unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def deleteTableIndex(self, table, name):
@@ -790,8 +861,9 @@ def createSpatialIndex(self, table, geom_column):
if self.isRasterTable(table):
return False
_, tablename = self.getSchemaTableName(table)
- sql = "SELECT CreateSpatialIndex(%s, %s)" % (
- self.quoteId(tablename), self.quoteId(geom_column))
+ sql = "SELECT CreateSpatialIndex({}, {})".format(
+ self.quoteId(tablename), self.quoteId(geom_column)
+ )
try:
res = self._fetchOne(sql)
except QgsProviderConnectionException:
@@ -802,8 +874,9 @@ def deleteSpatialIndex(self, table, geom_column):
if self.isRasterTable(table):
return False
_, tablename = self.getSchemaTableName(table)
- sql = "SELECT DisableSpatialIndex(%s, %s)" % (
- self.quoteId(tablename), self.quoteId(geom_column))
+ sql = "SELECT DisableSpatialIndex({}, {})".format(
+ self.quoteId(tablename), self.quoteId(geom_column)
+ )
res = self._fetchOne(sql)
return len(res) > 0 and len(res[0]) > 0 and res[0][0] == 1
@@ -813,14 +886,19 @@ def hasSpatialIndex(self, table, geom_column):
_, tablename = self.getSchemaTableName(table)
# (only available in >= 2.1.2)
- sql = "SELECT HasSpatialIndex(%s, %s)" % (self.quoteString(tablename), self.quoteString(geom_column))
+ sql = "SELECT HasSpatialIndex({}, {})".format(
+ self.quoteString(tablename), self.quoteString(geom_column)
+ )
gdal.PushErrorHandler()
ret = self._fetchOne(sql)
gdal.PopErrorHandler()
if len(ret) == 0:
# might be the case for GDAL < 2.1.2
- sql = "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name LIKE %s" % self.quoteString("%%rtree_" + tablename + "_%%")
+ sql = (
+ "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name LIKE %s"
+ % self.quoteString("%%rtree_" + tablename + "_%%")
+ )
ret = self._fetchOne(sql)
if len(ret) == 0:
return False
diff --git a/python/plugins/db_manager/db_plugins/gpkg/data_model.py b/python/plugins/db_manager/db_plugins/gpkg/data_model.py
index dd692703e927..a1a79a11ec1b 100644
--- a/python/plugins/db_manager/db_plugins/gpkg/data_model.py
+++ b/python/plugins/db_manager/db_plugins/gpkg/data_model.py
@@ -20,10 +20,12 @@
from qgis.core import QgsMessageLog
-from ..data_model import (TableDataModel,
- SqlResultModel,
- SqlResultModelAsync,
- SqlResultModelTask)
+from ..data_model import (
+ TableDataModel,
+ SqlResultModel,
+ SqlResultModelAsync,
+ SqlResultModelTask,
+)
from ..plugin import BaseError
diff --git a/python/plugins/db_manager/db_plugins/gpkg/info_model.py b/python/plugins/db_manager/db_plugins/gpkg/info_model.py
index cd346e5f8237..d0f2c31dca9b 100644
--- a/python/plugins/db_manager/db_plugins/gpkg/info_model.py
+++ b/python/plugins/db_manager/db_plugins/gpkg/info_model.py
@@ -31,7 +31,10 @@ def __init__(self, db):
def connectionDetails(self):
tbl = [
- (QApplication.translate("DBManagerPlugin", "Filename:"), self.db.connector.dbname)
+ (
+ QApplication.translate("DBManagerPlugin", "Filename:"),
+ self.db.connector.dbname,
+ )
]
return HtmlTable(tbl)
diff --git a/python/plugins/db_manager/db_plugins/gpkg/plugin.py b/python/plugins/db_manager/db_plugins/gpkg/plugin.py
index d6f63e8b3579..7fd5ee9ad904 100644
--- a/python/plugins/db_manager/db_plugins/gpkg/plugin.py
+++ b/python/plugins/db_manager/db_plugins/gpkg/plugin.py
@@ -33,8 +33,17 @@
)
from qgis.gui import QgsMessageBar
-from ..plugin import DBPlugin, Database, Table, VectorTable, RasterTable, TableField, TableIndex, TableTrigger, \
- InvalidDataException
+from ..plugin import (
+ DBPlugin,
+ Database,
+ Table,
+ VectorTable,
+ RasterTable,
+ TableField,
+ TableIndex,
+ TableTrigger,
+ InvalidDataException,
+)
def classFactory():
@@ -49,19 +58,19 @@ def icon(self):
@classmethod
def typeName(self):
- return 'gpkg'
+ return "gpkg"
@classmethod
def typeNameString(self):
- return QCoreApplication.translate('db_manager', 'GeoPackage')
+ return QCoreApplication.translate("db_manager", "GeoPackage")
@classmethod
def providerName(self):
- return 'ogr'
+ return "ogr"
@classmethod
def connectionSettingsKey(self):
- return 'providers/ogr/GPKG/connections'
+ return "providers/ogr/GPKG/connections"
def databasesFactory(self, connection, uri):
return GPKGDatabase(connection, uri)
@@ -73,7 +82,11 @@ def connect(self, parent=None):
conn = md.findConnection(conn_name)
if conn is None: # non-existent entry?
- raise InvalidDataException(self.tr('There is no defined database connection "{0}".').format(conn_name))
+ raise InvalidDataException(
+ self.tr('There is no defined database connection "{0}".').format(
+ conn_name
+ )
+ )
uri = QgsDataSourceUri()
uri.setDatabase(conn.uri())
@@ -90,8 +103,9 @@ def addConnection(self, conn_name, uri):
def addConnectionActionSlot(self, item, action, parent, index):
QApplication.restoreOverrideCursor()
try:
- filename, selected_filter = QFileDialog.getOpenFileName(parent,
- parent.tr("Choose GeoPackage file"), None, "GeoPackage (*.gpkg)")
+ filename, selected_filter = QFileDialog.getOpenFileName(
+ parent, parent.tr("Choose GeoPackage file"), None, "GeoPackage (*.gpkg)"
+ )
if not filename:
return
finally:
@@ -138,7 +152,9 @@ def sqlResultModelAsync(self, sql, parent):
def registerDatabaseActions(self, mainWindow):
action = QAction(self.tr("Run &Vacuum"), self)
- mainWindow.registerAction(action, self.tr("&Database"), self.runVacuumActionSlot)
+ mainWindow.registerAction(
+ action, self.tr("&Database"), self.runVacuumActionSlot
+ )
Database.registerDatabaseActions(self, mainWindow)
@@ -146,8 +162,11 @@ def runVacuumActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, (DBPlugin, Table)) or item.database() is None:
- parent.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ self.tr("No database selected or you are not connected to it."),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -172,10 +191,19 @@ def runAction(self, action):
def uniqueIdFunction(self):
return None
- def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, filter=""):
+ def toSqlLayer(
+ self,
+ sql,
+ geomCol,
+ uniqueCol,
+ layerName="QueryLayer",
+ layerType=None,
+ avoidSelectById=False,
+ filter="",
+ ):
from qgis.core import QgsVectorLayer
- vl = QgsVectorLayer(self.uri().database() + '|subset=' + sql, layerName, 'ogr')
+ vl = QgsVectorLayer(self.uri().database() + "|subset=" + sql, layerName, "ogr")
return vl
def supportsComment(self):
@@ -199,12 +227,12 @@ def __init__(self, row, db, schema=None):
self.name, self.isView, self.isSysTable = row
def ogrUri(self):
- ogrUri = "%s|layername=%s" % (self.uri().database(), self.name)
+ ogrUri = f"{self.uri().database()}|layername={self.name}"
return ogrUri
def mimeUri(self):
# QGIS has no provider to load Geopackage vectors, let's use OGR
- return "vector:ogr:%s:%s" % (self.name, self.ogrUri())
+ return f"vector:ogr:{self.name}:{self.ogrUri()}"
def toMapLayer(self, geometryType=None, crs=None):
from qgis.core import QgsVectorLayer
@@ -214,12 +242,12 @@ def toMapLayer(self, geometryType=None, crs=None):
if geometryType:
geom_mapping = {
- 'POINT': 'Point',
- 'LINESTRING': 'LineString',
- 'POLYGON': 'Polygon',
+ "POINT": "Point",
+ "LINESTRING": "LineString",
+ "POLYGON": "Polygon",
}
geometryType = geom_mapping[geometryType]
- uri = "{}|geometrytype={}".format(uri, geometryType)
+ uri = f"{uri}|geometrytype={geometryType}"
return QgsVectorLayer(uri, self.name, provider)
@@ -246,17 +274,23 @@ def __init__(self, row, db, schema=None):
# GPKG does case-insensitive checks for table names, but the
# GPKG provider didn't do the same in QGIS < 1.9, so self.geomTableName
# stores the table name like stored in the geometry_columns table
- self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[-5:]
- self.extent = self.database().connector.getTableExtent((self.schemaName(), self.name), self.geomColumn, force=False)
+ self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = (
+ row[-5:]
+ )
+ self.extent = self.database().connector.getTableExtent(
+ (self.schemaName(), self.name), self.geomColumn, force=False
+ )
def uri(self):
uri = self.database().uri()
- uri.setDataSource('', self.geomTableName, self.geomColumn)
+ uri.setDataSource("", self.geomTableName, self.geomColumn)
return uri
def hasSpatialIndex(self, geom_column=None):
geom_column = geom_column if geom_column is not None else self.geomColumn
- return self.database().connector.hasSpatialIndex((self.schemaName(), self.name), geom_column)
+ return self.database().connector.hasSpatialIndex(
+ (self.schemaName(), self.name), geom_column
+ )
def createSpatialIndex(self, geom_column=None):
self.aboutToChange.emit()
@@ -277,7 +311,9 @@ def refreshTableEstimatedExtent(self):
def refreshTableExtent(self):
prevExtent = self.extent
- self.extent = self.database().connector.getTableExtent((self.schemaName(), self.name), self.geomColumn, force=True)
+ self.extent = self.database().connector.getTableExtent(
+ (self.schemaName(), self.name), self.geomColumn, force=True
+ )
if self.extent != prevExtent:
self.refresh()
@@ -293,16 +329,18 @@ def __init__(self, row, db, schema=None):
GPKGTable.__init__(self, row[:-3], db, schema)
RasterTable.__init__(self, db, schema)
self.prefixName, self.geomColumn, self.srid = row[-3:]
- self.geomType = 'RASTER'
- self.extent = self.database().connector.getTableExtent((self.schemaName(), self.name), self.geomColumn)
+ self.geomType = "RASTER"
+ self.extent = self.database().connector.getTableExtent(
+ (self.schemaName(), self.name), self.geomColumn
+ )
def gpkgGdalUri(self):
- gdalUri = 'GPKG:%s:%s' % (self.uri().database(), self.prefixName)
+ gdalUri = f"GPKG:{self.uri().database()}:{self.prefixName}"
return gdalUri
def mimeUri(self):
# QGIS has no provider to load rasters, let's use GDAL
- uri = "raster:gdal:%s:%s" % (self.name, self.uri().database())
+ uri = f"raster:gdal:{self.name}:{self.uri().database()}"
return uri
def toMapLayer(self, geometryType=None, crs=None):
@@ -312,7 +350,9 @@ def toMapLayer(self, geometryType=None, crs=None):
uri = self.gpkgGdalUri()
rl = QgsRasterLayer(uri, self.name)
if rl.isValid():
- rl.setContrastEnhancement(QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum)
+ rl.setContrastEnhancement(
+ QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum
+ )
return rl
@@ -320,7 +360,14 @@ class GPKGTableField(TableField):
def __init__(self, row, table):
TableField.__init__(self, table)
- self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
+ (
+ self.num,
+ self.name,
+ self.dataType,
+ self.notNull,
+ self.default,
+ self.primaryKey,
+ ) = row
self.hasDefault = self.default
diff --git a/python/plugins/db_manager/db_plugins/gpkg/sql_dictionary.py b/python/plugins/db_manager/db_plugins/gpkg/sql_dictionary.py
index 514c804bb494..439a4645f1ed 100644
--- a/python/plugins/db_manager/db_plugins/gpkg/sql_dictionary.py
+++ b/python/plugins/db_manager/db_plugins/gpkg/sql_dictionary.py
@@ -18,9 +18,11 @@
def getSqlDictionary(spatial=True):
from ..spatialite.sql_dictionary import getSqlDictionary
+
return getSqlDictionary(spatial)
def getQueryBuilderDictionary():
from ..spatialite.sql_dictionary import getQueryBuilderDictionary
+
return getQueryBuilderDictionary()
diff --git a/python/plugins/db_manager/db_plugins/html_elems.py b/python/plugins/db_manager/db_plugins/html_elems.py
index 96b47f4c301e..69ab9fc6c91b 100644
--- a/python/plugins/db_manager/db_plugins/html_elems.py
+++ b/python/plugins/db_manager/db_plugins/html_elems.py
@@ -26,12 +26,12 @@ def __init__(self, data):
def toHtml(self):
if isinstance(self.data, list) or isinstance(self.data, tuple):
- html = ''
+ html = ""
for item in self.data:
html += HtmlContent(item).toHtml()
return html
- if hasattr(self.data, 'toHtml'):
+ if hasattr(self.data, "toHtml"):
return self.data.toHtml()
html = str(self.data).replace("\n", " ")
@@ -46,7 +46,7 @@ def hasContents(self):
break
return not empty
- if hasattr(self.data, 'hasContents'):
+ if hasattr(self.data, "hasContents"):
return self.data.hasContents()
return len(self.data) > 0
@@ -58,9 +58,9 @@ def __init__(self, tag, data, attrs=None):
self.tag = tag
self.data = data if isinstance(data, HtmlContent) else HtmlContent(data)
self.attrs = attrs if attrs is not None else dict()
- if 'tag' in self.attrs:
- self.setTag(self.attrs['tag'])
- del self.attrs['tag']
+ if "tag" in self.attrs:
+ self.setTag(self.attrs["tag"])
+ del self.attrs["tag"]
def setTag(self, tag):
self.tag = tag
@@ -72,19 +72,21 @@ def setAttr(self, name, value):
self.attrs[name] = value
def getAttrsHtml(self):
- html = ''
+ html = ""
for k, v in self.attrs.items():
- html += ' %s="%s"' % (k, v)
+ html += f' {k}="{v}"'
return html
def openTagHtml(self):
- return "<%s%s>" % (self.tag, self.getAttrsHtml())
+ return f"<{self.tag}{self.getAttrsHtml()}>"
def closeTagHtml(self):
return "%s>" % self.tag
def toHtml(self):
- return "%s%s%s" % (self.openTagHtml(), self.data.toHtml(), self.closeTagHtml())
+ return "{}{}{}".format(
+ self.openTagHtml(), self.data.toHtml(), self.closeTagHtml()
+ )
def hasContents(self):
return self.data.toHtml() != ""
@@ -93,13 +95,13 @@ def hasContents(self):
class HtmlParagraph(HtmlElem):
def __init__(self, data, attrs=None):
- HtmlElem.__init__(self, 'p', data, attrs)
+ HtmlElem.__init__(self, "p", data, attrs)
class HtmlListItem(HtmlElem):
def __init__(self, data, attrs=None):
- HtmlElem.__init__(self, 'li', data, attrs)
+ HtmlElem.__init__(self, "li", data, attrs)
class HtmlList(HtmlElem):
@@ -110,13 +112,13 @@ def __init__(self, items, attrs=None):
for i, item in enumerate(items):
if not isinstance(item, HtmlListItem):
items[i] = HtmlListItem(item)
- HtmlElem.__init__(self, 'ul', items, attrs)
+ HtmlElem.__init__(self, "ul", items, attrs)
class HtmlTableCol(HtmlElem):
def __init__(self, data, attrs=None):
- HtmlElem.__init__(self, 'td', data, attrs)
+ HtmlElem.__init__(self, "td", data, attrs)
def closeTagHtml(self):
# FIX INVALID BEHAVIOR: an empty cell as last table's cell break margins
@@ -131,7 +133,7 @@ def __init__(self, cols, attrs=None):
for i, c in enumerate(cols):
if not isinstance(c, HtmlTableCol):
cols[i] = HtmlTableCol(c)
- HtmlElem.__init__(self, 'tr', cols, attrs)
+ HtmlElem.__init__(self, "tr", cols, attrs)
class HtmlTableHeader(HtmlTableRow):
@@ -139,7 +141,7 @@ class HtmlTableHeader(HtmlTableRow):
def __init__(self, cols, attrs=None):
HtmlTableRow.__init__(self, cols, attrs)
for c in self.getOriginalData():
- c.setTag('th')
+ c.setTag("th")
class HtmlTable(HtmlElem):
@@ -150,14 +152,14 @@ def __init__(self, rows, attrs=None):
for i, r in enumerate(rows):
if not isinstance(r, HtmlTableRow):
rows[i] = HtmlTableRow(r)
- HtmlElem.__init__(self, 'table', rows, attrs)
+ HtmlElem.__init__(self, "table", rows, attrs)
class HtmlSection(HtmlContent):
def __init__(self, title, content=None):
- data = ['
', title, '
']
+ data = ['
', title, "
"]
if content is not None:
- data.extend(['
', content, '
'])
- data.append('
')
+ data.extend(["
", content, "
"])
+ data.append("
")
HtmlContent.__init__(self, data)
diff --git a/python/plugins/db_manager/db_plugins/info_model.py b/python/plugins/db_manager/db_plugins/info_model.py
index c27d77b521b3..cf3af0c1753a 100644
--- a/python/plugins/db_manager/db_plugins/info_model.py
+++ b/python/plugins/db_manager/db_plugins/info_model.py
@@ -20,7 +20,15 @@
from qgis.PyQt.QtWidgets import QApplication
-from .html_elems import HtmlContent, HtmlSection, HtmlParagraph, HtmlList, HtmlTable, HtmlTableHeader, HtmlTableCol
+from .html_elems import (
+ HtmlContent,
+ HtmlSection,
+ HtmlParagraph,
+ HtmlList,
+ HtmlTable,
+ HtmlTableHeader,
+ HtmlTableCol,
+)
class DatabaseInfo:
@@ -33,15 +41,19 @@ def __del__(self):
def generalInfo(self):
info = self.db.connector.getInfo()
- tbl = [
- (QApplication.translate("DBManagerPlugin", "Server version: "), info[0])
- ]
+ tbl = [(QApplication.translate("DBManagerPlugin", "Server version: "), info[0])]
return HtmlTable(tbl)
def connectionDetails(self):
tbl = [
- (QApplication.translate("DBManagerPlugin", "Host:"), self.db.connector.host),
- (QApplication.translate("DBManagerPlugin", "User:"), self.db.connector.user)
+ (
+ QApplication.translate("DBManagerPlugin", "Host:"),
+ self.db.connector.host,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "User:"),
+ self.db.connector.user,
+ ),
]
return HtmlTable(tbl)
@@ -55,14 +67,20 @@ def spatialInfo(self):
tbl = [
(QApplication.translate("DBManagerPlugin", "Library:"), info[0]),
("GEOS:", info[1]),
- ("Proj:", info[2])
+ ("Proj:", info[2]),
]
ret.append(HtmlTable(tbl))
if not self.db.connector.has_geometry_columns:
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " geometry_columns table doesn't exist!\n"
- "This table is essential for many GIS applications for enumeration of tables.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " geometry_columns table doesn't exist!\n"
+ "This table is essential for many GIS applications for enumeration of tables.",
+ )
+ )
+ )
return ret
@@ -72,12 +90,16 @@ def privilegesDetails(self):
if details[0]:
lst.append(QApplication.translate("DBManagerPlugin", "create new schemas"))
if details[1]:
- lst.append(QApplication.translate("DBManagerPlugin", "create temporary tables"))
+ lst.append(
+ QApplication.translate("DBManagerPlugin", "create temporary tables")
+ )
return HtmlList(lst)
def toHtml(self):
if self.db is None:
- return HtmlSection(QApplication.translate("DBManagerPlugin", ' Not connected')).toHtml()
+ return HtmlSection(
+ QApplication.translate("DBManagerPlugin", " Not connected")
+ ).toHtml()
ret = []
@@ -86,14 +108,24 @@ def toHtml(self):
if conn_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Connection details'), conn_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Connection details"),
+ conn_details,
+ )
+ )
# database information
general_info = self.generalInfo()
if general_info is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'General info'), general_info))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "General info"),
+ general_info,
+ )
+ )
# has spatial enabled?
spatial_info = self.spatialInfo()
@@ -103,7 +135,9 @@ def toHtml(self):
typename = self.db.connection().typeNameString()
spatial_info = HtmlContent(spatial_info)
if not spatial_info.hasContents():
- spatial_info = QApplication.translate("DBManagerPlugin", ' {0} support not enabled!').format(typename)
+ spatial_info = QApplication.translate(
+ "DBManagerPlugin", " {0} support not enabled!"
+ ).format(typename)
ret.append(HtmlSection(typename, spatial_info))
# privileges
@@ -113,10 +147,20 @@ def toHtml(self):
else:
priv_details = HtmlContent(priv_details)
if not priv_details.hasContents():
- priv_details = QApplication.translate("DBManagerPlugin", ' This user has no privileges!')
+ priv_details = QApplication.translate(
+ "DBManagerPlugin", " This user has no privileges!"
+ )
else:
- priv_details = [QApplication.translate("DBManagerPlugin", "User has privileges:"), priv_details]
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Privileges'), priv_details))
+ priv_details = [
+ QApplication.translate("DBManagerPlugin", "User has privileges:"),
+ priv_details,
+ ]
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Privileges"),
+ priv_details,
+ )
+ )
return HtmlContent(ret).toHtml()
@@ -134,9 +178,16 @@ def generalInfo(self):
# ("Tables:", self.schema.tableCount)
]
if self.schema.owner:
- tbl.append((QApplication.translate("DBManagerPlugin", "Owner:"), self.schema.owner))
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Owner:"), self.schema.owner)
+ )
if self.schema.comment:
- tbl.append((QApplication.translate("DBManagerPlugin", "Comment:"), self.schema.comment))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Comment:"),
+ self.schema.comment,
+ )
+ )
return HtmlTable(tbl)
def privilegesDetails(self):
@@ -155,7 +206,12 @@ def toHtml(self):
if general_info is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Schema details'), general_info))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Schema details"),
+ general_info,
+ )
+ )
priv_details = self.privilegesDetails()
if priv_details is None:
@@ -163,11 +219,21 @@ def toHtml(self):
else:
priv_details = HtmlContent(priv_details)
if not priv_details.hasContents():
- priv_details = QApplication.translate("DBManagerPlugin",
- ' This user has no privileges to access this schema!')
+ priv_details = QApplication.translate(
+ "DBManagerPlugin",
+ " This user has no privileges to access this schema!",
+ )
else:
- priv_details = [QApplication.translate("DBManagerPlugin", "User has privileges:"), priv_details]
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Privileges'), priv_details))
+ priv_details = [
+ QApplication.translate("DBManagerPlugin", "User has privileges:"),
+ priv_details,
+ ]
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Privileges"),
+ priv_details,
+ )
+ )
return HtmlContent(ret).toHtml()
@@ -189,15 +255,33 @@ def generalInfo(self):
self.table.blockSignals(False)
tbl = [
- (QApplication.translate("DBManagerPlugin", "Relation type:"),
- QApplication.translate("DBManagerPlugin", "View") if self.table.isView else QApplication.translate(
- "DBManagerPlugin", "Table")),
- (QApplication.translate("DBManagerPlugin", "Rows:"),
- self.table.rowCount if self.table.rowCount is not None else QApplication.translate("DBManagerPlugin",
- 'Unknown (find out)'))
+ (
+ QApplication.translate("DBManagerPlugin", "Relation type:"),
+ (
+ QApplication.translate("DBManagerPlugin", "View")
+ if self.table.isView
+ else QApplication.translate("DBManagerPlugin", "Table")
+ ),
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "Rows:"),
+ (
+ self.table.rowCount
+ if self.table.rowCount is not None
+ else QApplication.translate(
+ "DBManagerPlugin",
+ 'Unknown (find out)',
+ )
+ ),
+ ),
]
if self.table.comment:
- tbl.append((QApplication.translate("DBManagerPlugin", "Comment:"), self.table.comment))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Comment:"),
+ self.table.comment,
+ )
+ )
return HtmlTable(tbl)
@@ -209,8 +293,12 @@ def fieldsDetails(self):
# define the table header
header = (
- "#", QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Type"),
- QApplication.translate("DBManagerPlugin", "Null"), QApplication.translate("DBManagerPlugin", "Default"))
+ "#",
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Null"),
+ QApplication.translate("DBManagerPlugin", "Default"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
@@ -221,7 +309,9 @@ def fieldsDetails(self):
attrs = {"class": "underline"} if fld.primaryKey else None
name = HtmlTableCol(fld.name, attrs)
- tbl.append((fld.num, name, fld.type2String(), is_null_txt, fld.default2String()))
+ tbl.append(
+ (fld.num, name, fld.type2String(), is_null_txt, fld.default2String())
+ )
return HtmlTable(tbl, {"class": "header"})
@@ -232,15 +322,21 @@ def constraintsDetails(self):
tbl = []
# define the table header
- header = (QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Type"),
- QApplication.translate("DBManagerPlugin", "Column(s)"))
+ header = (
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Column(s)"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for con in self.table.constraints():
# get the fields the constraint is defined on
- cols = [p[1].name if p[1] is not None else "??? (#%d)" % p[0] for p in iter(list(con.fields().items()))]
- tbl.append((con.name, con.type2String(), '\n'.join(cols)))
+ cols = [
+ p[1].name if p[1] is not None else "??? (#%d)" % p[0]
+ for p in iter(list(con.fields().items()))
+ ]
+ tbl.append((con.name, con.type2String(), "\n".join(cols)))
return HtmlTable(tbl, {"class": "header"})
@@ -252,14 +348,19 @@ def indexesDetails(self):
# define the table header
header = (
- QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Column(s)"))
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Column(s)"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for idx in self.table.indexes():
# get the fields the index is defined on
- cols = [p[1].name if p[1] is not None else "??? (#%d)" % p[0] for p in iter(list(idx.fields().items()))]
- tbl.append((idx.name, '\n'.join(cols)))
+ cols = [
+ p[1].name if p[1] is not None else "??? (#%d)" % p[0]
+ for p in iter(list(idx.fields().items()))
+ ]
+ tbl.append((idx.name, "\n".join(cols)))
return HtmlTable(tbl, {"class": "header"})
@@ -271,21 +372,28 @@ def triggersDetails(self):
# define the table header
header = (
- QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Function"))
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Function"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for trig in self.table.triggers():
- name = '%(name)s (%(action)s)' % {"name": trig.name,
- "action": "delete"}
- tbl.append((name, trig.function.replace('<', '<')))
+ name = (
+ '{name} ({action})'.format(
+ name=trig.name, action="delete"
+ )
+ )
+ tbl.append((name, trig.function.replace("<", "<")))
return HtmlTable(tbl, {"class": "header"})
def getViewDefinition(self):
if not self.table.isView:
return None
- return self.table.database().connector.getViewDefinition((self.table.schemaName(), self.table.name))
+ return self.table.database().connector.getViewDefinition(
+ (self.table.schemaName(), self.table.name)
+ )
def getTableInfo(self):
ret = []
@@ -294,7 +402,12 @@ def getTableInfo(self):
if general_info is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'General info'), general_info))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "General info"),
+ general_info,
+ )
+ )
# spatial info
spatial_info = self.spatialInfo()
@@ -303,36 +416,61 @@ def getTableInfo(self):
else:
spatial_info = HtmlContent(spatial_info)
if not spatial_info.hasContents():
- spatial_info = QApplication.translate("DBManagerPlugin", ' This is not a spatial table.')
- ret.append(HtmlSection(self.table.database().connection().typeNameString(), spatial_info))
+ spatial_info = QApplication.translate(
+ "DBManagerPlugin", " This is not a spatial table."
+ )
+ ret.append(
+ HtmlSection(
+ self.table.database().connection().typeNameString(), spatial_info
+ )
+ )
# fields
fields_details = self.fieldsDetails()
if fields_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Fields'), fields_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Fields"), fields_details
+ )
+ )
# constraints
constraints_details = self.constraintsDetails()
if constraints_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Constraints'), constraints_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Constraints"),
+ constraints_details,
+ )
+ )
# indexes
indexes_details = self.indexesDetails()
if indexes_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Indexes'), indexes_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Indexes"),
+ indexes_details,
+ )
+ )
# triggers
triggers_details = self.triggersDetails()
if triggers_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Triggers'), triggers_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Triggers"),
+ triggers_details,
+ )
+ )
return ret
@@ -347,7 +485,12 @@ def getViewInfo(self):
if view_def is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'View definition'), view_def))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "View definition"),
+ view_def,
+ )
+ )
return ret
@@ -370,19 +513,38 @@ def spatialInfo(self):
return ret
tbl = [
- (QApplication.translate("DBManagerPlugin", "Column:"), self.table.geomColumn),
- (QApplication.translate("DBManagerPlugin", "Geometry:"), self.table.geomType)
+ (
+ QApplication.translate("DBManagerPlugin", "Column:"),
+ self.table.geomColumn,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "Geometry:"),
+ self.table.geomType,
+ ),
]
# only if we have info from geometry_columns
if self.table.geomDim:
- tbl.append((QApplication.translate("DBManagerPlugin", "Dimension:"), self.table.geomDim))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Dimension:"),
+ self.table.geomDim,
+ )
+ )
srid = self.table.srid if self.table.srid not in (None, 0) else -1
- sr_info = self.table.database().connector.getSpatialRefInfo(srid) if srid != -1 else QApplication.translate(
- "DBManagerPlugin", "Undefined")
+ sr_info = (
+ self.table.database().connector.getSpatialRefInfo(srid)
+ if srid != -1
+ else QApplication.translate("DBManagerPlugin", "Undefined")
+ )
if sr_info:
- tbl.append((QApplication.translate("DBManagerPlugin", "Spatial ref:"), "%s (%d)" % (sr_info, srid)))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Spatial ref:"),
+ "%s (%d)" % (sr_info, srid),
+ )
+ )
# estimated extent
if not self.table.isView:
@@ -393,36 +555,62 @@ def spatialInfo(self):
self.table.refreshTableEstimatedExtent()
self.table.blockSignals(False)
- if self.table.estimatedExtent is not None and self.table.estimatedExtent[0] is not None:
+ if (
+ self.table.estimatedExtent is not None
+ and self.table.estimatedExtent[0] is not None
+ ):
if isinstance(self.table.estimatedExtent, list):
- estimated_extent_str = ', '.join('%.5f' % e for e in self.table.estimatedExtent)
+ estimated_extent_str = ", ".join(
+ "%.5f" % e for e in self.table.estimatedExtent
+ )
else:
- estimated_extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.estimatedExtent
- tbl.append((QApplication.translate("DBManagerPlugin", "Estimated extent:"), estimated_extent_str))
+ estimated_extent_str = (
+ "%.5f, %.5f - %.5f, %.5f" % self.table.estimatedExtent
+ )
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Estimated extent:"),
+ estimated_extent_str,
+ )
+ )
# extent
if self.table.extent is not None and self.table.extent[0] is not None:
if isinstance(self.table.extent, list):
- extent_str = ', '.join('%.5f' % e for e in self.table.extent)
+ extent_str = ", ".join("%.5f" % e for e in self.table.extent)
else:
- extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.extent
+ extent_str = "%.5f, %.5f - %.5f, %.5f" % self.table.extent
else:
- extent_str = QApplication.translate("DBManagerPlugin",
- '(unknown) (find out)')
+ extent_str = QApplication.translate(
+ "DBManagerPlugin",
+ '(unknown) (find out)',
+ )
tbl.append((QApplication.translate("DBManagerPlugin", "Extent:"), extent_str))
ret.append(HtmlTable(tbl))
# is there an entry in geometry_columns?
- if self.table.geomType.lower() == 'geometry':
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " There is no entry in geometry_columns!")))
+ if self.table.geomType.lower() == "geometry":
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " There is no entry in geometry_columns!",
+ )
+ )
+ )
# find out whether the geometry column has spatial index on it
if not self.table.isView:
if not self.table.hasSpatialIndex():
- ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
- ' No spatial index defined (create it)')))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ ' No spatial index defined (create it)',
+ )
+ )
+ )
return ret
@@ -438,23 +626,39 @@ def spatialInfo(self):
return ret
tbl = [
- (QApplication.translate("DBManagerPlugin", "Column:"), self.table.geomColumn),
- (QApplication.translate("DBManagerPlugin", "Geometry:"), self.table.geomType)
+ (
+ QApplication.translate("DBManagerPlugin", "Column:"),
+ self.table.geomColumn,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "Geometry:"),
+ self.table.geomType,
+ ),
]
# only if we have info from geometry_columns
srid = self.table.srid if self.table.srid is not None else -1
- sr_info = self.table.database().connector.getSpatialRefInfo(srid) if srid != -1 else QApplication.translate(
- "DBManagerPlugin", "Undefined")
+ sr_info = (
+ self.table.database().connector.getSpatialRefInfo(srid)
+ if srid != -1
+ else QApplication.translate("DBManagerPlugin", "Undefined")
+ )
if sr_info:
- tbl.append((QApplication.translate("DBManagerPlugin", "Spatial ref:"), "%s (%d)" % (sr_info, srid)))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Spatial ref:"),
+ "%s (%d)" % (sr_info, srid),
+ )
+ )
# extent
if self.table.extent is not None and self.table.extent[0] is not None:
- extent_str = '%.5f, %.5f - %.5f, %.5f' % self.table.extent
+ extent_str = "%.5f, %.5f - %.5f, %.5f" % self.table.extent
else:
- extent_str = QApplication.translate("DBManagerPlugin",
- '(unknown) (find out)')
+ extent_str = QApplication.translate(
+ "DBManagerPlugin",
+ '(unknown) (find out)',
+ )
tbl.append((QApplication.translate("DBManagerPlugin", "Extent:"), extent_str))
ret.append(HtmlTable(tbl))
diff --git a/python/plugins/db_manager/db_plugins/oracle/QtSqlDB.py b/python/plugins/db_manager/db_plugins/oracle/QtSqlDB.py
index 9e37ce98946c..0f96119e3751 100644
--- a/python/plugins/db_manager/db_plugins/oracle/QtSqlDB.py
+++ b/python/plugins/db_manager/db_plugins/oracle/QtSqlDB.py
@@ -112,15 +112,17 @@ def execute(self, operation, parameters=[]):
else:
continue
- self.description.append([
- f.name(), # name
- t, # type_code
- f.length(), # display_size
- f.length(), # internal_size
- f.precision(), # precision
- None, # scale
- f.requiredStatus() != QSqlField.RequiredStatus.Required # null_ok
- ])
+ self.description.append(
+ [
+ f.name(), # name
+ t, # type_code
+ f.length(), # display_size
+ f.length(), # internal_size
+ f.precision(), # precision
+ None, # scale
+ f.requiredStatus() != QSqlField.RequiredStatus.Required, # null_ok
+ ]
+ )
def executemany(self, operation, seq_of_parameters):
if len(seq_of_parameters) == 0:
@@ -146,9 +148,11 @@ def fetchone(self):
row = []
for i in range(len(self.description)):
value = self.qry.value(i)
- if (isinstance(value, QDate) or
- isinstance(value, QTime) or
- isinstance(value, QDateTime)):
+ if (
+ isinstance(value, QDate)
+ or isinstance(value, QTime)
+ or isinstance(value, QDateTime)
+ ):
value = value.toString()
elif isinstance(value, QByteArray):
value = "GEOMETRY"
@@ -190,7 +194,8 @@ class QtSqlDBConnection:
def __init__(self, driver, dbname, user, passwd):
self.conn = QSqlDatabase.addDatabase(
- driver, "qtsql_%d" % QtSqlDBConnection.connections)
+ driver, "qtsql_%d" % QtSqlDBConnection.connections
+ )
QtSqlDBConnection.connections += 1
self.conn.setDatabaseName(dbname)
self.conn.setUserName(user)
diff --git a/python/plugins/db_manager/db_plugins/oracle/connector.py b/python/plugins/db_manager/db_plugins/oracle/connector.py
index 0055fac61ef5..ba2c61e31176 100644
--- a/python/plugins/db_manager/db_plugins/oracle/connector.py
+++ b/python/plugins/db_manager/db_plugins/oracle/connector.py
@@ -54,14 +54,14 @@ class OracleDBConnector(DBConnector):
3003: QgsWkbTypes.Type.Polygon25D,
3005: QgsWkbTypes.Type.MultiPoint25D,
3006: QgsWkbTypes.Type.MultiLineString25D,
- 3007: QgsWkbTypes.Type.MultiPolygon25D
+ 3007: QgsWkbTypes.Type.MultiPolygon25D,
}
def __init__(self, uri, connName):
DBConnector.__init__(self, uri)
self.connName = connName
- self.user = uri.username() or os.environ.get('USER')
+ self.user = uri.username() or os.environ.get("USER")
self.passwd = uri.password()
self.host = uri.host()
@@ -76,29 +76,29 @@ def __init__(self, uri, connName):
# Connection options
self.useEstimatedMetadata = uri.useEstimatedMetadata()
- self.userTablesOnly = uri.param('userTablesOnly').lower() == "true"
- self.geometryColumnsOnly = uri.param(
- 'geometryColumnsOnly').lower() == "true"
- self.allowGeometrylessTables = uri.param(
- 'allowGeometrylessTables').lower() == "true"
- self.onlyExistingTypes = uri.param(
- 'onlyExistingTypes').lower() == "true"
- self.includeGeoAttributes = uri.param(
- 'includeGeoAttributes').lower() == "true"
+ self.userTablesOnly = uri.param("userTablesOnly").lower() == "true"
+ self.geometryColumnsOnly = uri.param("geometryColumnsOnly").lower() == "true"
+ self.allowGeometrylessTables = (
+ uri.param("allowGeometrylessTables").lower() == "true"
+ )
+ self.onlyExistingTypes = uri.param("onlyExistingTypes").lower() == "true"
+ self.includeGeoAttributes = uri.param("includeGeoAttributes").lower() == "true"
# For refreshing
self.populated = False
try:
self.connection = QtSqlDB.connect(
- "QOCISPATIAL", self.dbname, self.user, self.passwd)
+ "QOCISPATIAL", self.dbname, self.user, self.passwd
+ )
except self.connection_error_types() as e:
raise ConnectionError(e)
# Find if we can connect to data_sources_cache.db
sqlite_cache_file = os.path.join(
- QgsApplication.qgisSettingsDirPath(), "data_sources_cache.db")
- if (os.path.isfile(sqlite_cache_file)):
+ QgsApplication.qgisSettingsDirPath(), "data_sources_cache.db"
+ )
+ if os.path.isfile(sqlite_cache_file):
try:
self.cache_connection = sqlite3.connect(sqlite_cache_file)
except sqlite3.Error:
@@ -110,8 +110,9 @@ def __init__(self, uri, connName):
if self.cache_connection:
try:
cache_c = self.cache_connection.cursor()
- query = ("SELECT COUNT(*) FROM meta_oracle WHERE"
- " conn = '{}'".format(self.connName))
+ query = "SELECT COUNT(*) FROM meta_oracle WHERE" " conn = '{}'".format(
+ self.connName
+ )
cache_c.execute(query)
has_cached = cache_c.fetchone()[0]
cache_c.close()
@@ -129,8 +130,10 @@ def _connectionInfo(self):
def _checkSpatial(self):
"""Check whether Oracle Spatial is present in catalog."""
- query = ("SELECT count(*) FROM v$option WHERE parameter = "
- " 'Spatial' AND value = 'TRUE'")
+ query = (
+ "SELECT count(*) FROM v$option WHERE parameter = "
+ " 'Spatial' AND value = 'TRUE'"
+ )
c = self._execute(None, query)
self.has_spatial = self._fetchone(c)[0] > 0
c.close()
@@ -140,12 +143,12 @@ def _checkSpatial(self):
def _checkGeometryColumnsTable(self):
"""Check if user can read *_SDO_GEOM_METADATA view."""
# First check if user can read ALL_SDO_GEOM_METADATA
- privs = self.getRawTablePrivileges('ALL_SDO_GEOM_METADATA',
- 'MDSYS', 'PUBLIC')
+ privs = self.getRawTablePrivileges("ALL_SDO_GEOM_METADATA", "MDSYS", "PUBLIC")
# Otherwise, try with USER_SDO_GEOM_METADATA
if not privs[0]:
- privs = self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
- 'MDSYS', 'PUBLIC')
+ privs = self.getRawTablePrivileges(
+ "USER_SDO_GEOM_METADATA", "MDSYS", "PUBLIC"
+ )
if privs[0]:
self.has_geometry_columns = True
@@ -211,12 +214,18 @@ def fieldTypes(self):
http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CNCPT1828
"""
return [
- "number", "number(9)", # integers
- "number(9,2)", "number(*,4)", "binary_float",
+ "number",
+ "number(9)", # integers
+ "number(9,2)",
+ "number(*,4)",
+ "binary_float",
"binary_double", # floats
- "varchar2(255)", "char(20)", "nvarchar2(255)",
+ "varchar2(255)",
+ "char(20)",
+ "nvarchar2(255)",
"nchar(20)", # strings
- "date", "timestamp" # date/time
+ "date",
+ "timestamp", # date/time
]
def getSchemaPrivileges(self, schema):
@@ -248,10 +257,12 @@ def getRawTablePrivileges(self, table, owner, grantee):
AND TABLE_NAME = {}
AND OWNER = {}
AND GRANTEE IN ({}, {})
- """.format(self.quoteString(table),
- self.quoteString(owner),
- self.quoteString(grantee),
- self.quoteString(grantee.upper()))
+ """.format(
+ self.quoteString(table),
+ self.quoteString(owner),
+ self.quoteString(grantee),
+ self.quoteString(grantee.upper()),
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -284,7 +295,9 @@ def getSchemasCache(self):
SELECT DISTINCT ownername
FROM "oracle_{}"
ORDER BY ownername
- """.format(self.connName)
+ """.format(
+ self.connName
+ )
c = self.cache_connection.cursor()
c.execute(sql)
res = c.fetchall()
@@ -303,13 +316,11 @@ def getSchemas(self):
return self.getSchemasCache()
# Use cache if available:
- metatable = ("all_objects WHERE object_type IN "
- "('TABLE','VIEW','SYNONYM')")
+ metatable = "all_objects WHERE object_type IN " "('TABLE','VIEW','SYNONYM')"
if self.geometryColumnsOnly:
metatable = "all_sdo_geom_metadata"
- sql = """SELECT DISTINCT owner FROM {} ORDER BY owner""".format(
- metatable)
+ sql = f"""SELECT DISTINCT owner FROM {metatable} ORDER BY owner"""
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -343,8 +354,7 @@ def getTables(self, schema=None, add_sys_tables=False):
prefix = "USER"
owner = "user As OWNER"
if schema and not self.userTablesOnly:
- where = "AND o.owner = {} ".format(
- self.quoteString(schema))
+ where = f"AND o.owner = {self.quoteString(schema)} "
sql = """
SELECT o.OBJECT_NAME, {},
@@ -355,9 +365,12 @@ def getTables(self, schema=None, add_sys_tables=False):
WHERE o.object_type IN ('TABLE','VIEW','SYNONYM')
{} {}
ORDER BY o.OBJECT_NAME
- """.format(owner, prefix, where,
- "" if add_sys_tables
- else "AND o.OBJECT_NAME NOT LIKE 'MDRT_%'")
+ """.format(
+ owner,
+ prefix,
+ where,
+ "" if add_sys_tables else "AND o.OBJECT_NAME NOT LIKE 'MDRT_%'",
+ )
c = self._execute(None, sql)
for tbl in self._fetchall(c):
@@ -370,7 +383,9 @@ def getTables(self, schema=None, add_sys_tables=False):
self.populated = True
- listTables = sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
+ listTables = sorted(
+ items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1]))
+ )
if self.hasCache():
self.updateCache(listTables, schema)
@@ -393,23 +408,25 @@ def getTablesCache(self, schema=None):
pass
if not self.allowGeometrylessTables:
- return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
+ return sorted(
+ items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1]))
+ )
# get all non geographic tables and views
schema_where = ""
if self.userTablesOnly:
- schema_where = "AND ownername = '{}'".format(
- self.user)
+ schema_where = f"AND ownername = '{self.user}'"
if schema and not self.userTablesOnly:
- schema_where = "AND ownername = '{}'".format(
- schema)
+ schema_where = f"AND ownername = '{schema}'"
sql = """
SELECT tablename, ownername, isview
FROM "oracle_{}"
WHERE geometrycolname IS '' {}
ORDER BY tablename
- """.format(self.connName, schema_where)
+ """.format(
+ self.connName, schema_where
+ )
c = self.cache_connection.cursor()
c.execute(sql)
@@ -435,18 +452,28 @@ def updateCache(self, tableList, schema=None):
pkCols = self.pkCols((schema, table[1]))
# Deals with non-geographic tables
if table[0] == Table.TableType:
- line = (table[1], table[2], int(table[3]),
- "",
- ",".join(pkCols) if pkCols else "",
- 100, 0, "")
+ line = (
+ table[1],
+ table[2],
+ int(table[3]),
+ "",
+ ",".join(pkCols) if pkCols else "",
+ 100,
+ 0,
+ "",
+ )
# Deals with vector tables
elif table[0] == Table.VectorType:
- line = (table[1], table[2], int(table[3]),
- table[4],
- ",".join(pkCols) if pkCols else "",
- table[9],
- table[8] if table[10] == "-1" else table[10],
- "")
+ line = (
+ table[1],
+ table[2],
+ int(table[3]),
+ table[4],
+ ",".join(pkCols) if pkCols else "",
+ table[9],
+ table[8] if table[10] == "-1" else table[10],
+ "",
+ )
else:
continue
data.append(line)
@@ -454,8 +481,9 @@ def updateCache(self, tableList, schema=None):
# Then, empty the cache list
sql = """
DELETE FROM "oracle_{}" {}
- """.format(self.connName,
- "WHERE ownername = '{}'".format(schema) if schema else "")
+ """.format(
+ self.connName, f"WHERE ownername = '{schema}'" if schema else ""
+ )
self.cache_connection.execute(sql)
self.cache_connection.commit()
@@ -464,7 +492,9 @@ def updateCache(self, tableList, schema=None):
INSERT INTO "oracle_{}"(tablename, ownername, isview,
geometrycolname, pkcols, geomtypes, geomsrids, sql)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
- """.format(self.connName)
+ """.format(
+ self.connName
+ )
c = self.cache_connection.cursor()
c.executemany(sql, data)
c.close()
@@ -472,16 +502,22 @@ def updateCache(self, tableList, schema=None):
def singleGeomTypes(self, geomtypes, srids):
"""Intelligent wkbtype grouping (multi with non multi)"""
- if (QgsWkbTypes.Type.Polygon in geomtypes
- and QgsWkbTypes.Type.MultiPolygon in geomtypes):
+ if (
+ QgsWkbTypes.Type.Polygon in geomtypes
+ and QgsWkbTypes.Type.MultiPolygon in geomtypes
+ ):
srids.pop(geomtypes.index(QgsWkbTypes.Type.Polygon))
geomtypes.pop(geomtypes.index(QgsWkbTypes.Type.Polygon))
- if (QgsWkbTypes.Type.Point in geomtypes
- and QgsWkbTypes.Type.MultiPoint in geomtypes):
+ if (
+ QgsWkbTypes.Type.Point in geomtypes
+ and QgsWkbTypes.Type.MultiPoint in geomtypes
+ ):
srids.pop(geomtypes.index(QgsWkbTypes.Type.Point))
geomtypes.pop(geomtypes.index(QgsWkbTypes.Type.Point))
- if (QgsWkbTypes.Type.LineString in geomtypes
- and QgsWkbTypes.Type.MultiLineString in geomtypes):
+ if (
+ QgsWkbTypes.Type.LineString in geomtypes
+ and QgsWkbTypes.Type.MultiLineString in geomtypes
+ ):
srids.pop(geomtypes.index(QgsWkbTypes.Type.LineString))
geomtypes.pop(geomtypes.index(QgsWkbTypes.Type.LineString))
if QgsWkbTypes.Type.Unknown in geomtypes and len(geomtypes) > 1:
@@ -502,11 +538,9 @@ def getVectorTablesCache(self, schema=None):
"""
schema_where = ""
if self.userTablesOnly:
- schema_where = "AND ownername = '{}'".format(
- self.user)
+ schema_where = f"AND ownername = '{self.user}'"
if schema and not self.userTablesOnly:
- schema_where = "AND ownername = '{}'".format(
- schema)
+ schema_where = f"AND ownername = '{schema}'"
sql = """
SELECT tablename, ownername, isview,
@@ -515,7 +549,9 @@ def getVectorTablesCache(self, schema=None):
FROM "oracle_{}"
WHERE geometrycolname IS NOT '' {}
ORDER BY tablename
- """.format(self.connName, schema_where)
+ """.format(
+ self.connName, schema_where
+ )
items = []
@@ -538,7 +574,9 @@ def getVectorTablesCache(self, schema=None):
buf = list(item)
geomtype = geomtypes[j]
srid = srids[j]
- datatype = QgsWkbTypes.displayString(QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype)))
+ datatype = QgsWkbTypes.displayString(
+ QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype))
+ )
geo = datatype.upper()
buf.append(geo)
buf.append(geomtype)
@@ -571,8 +609,8 @@ def getVectorTables(self, schema=None):
where = "WHERE c.data_type = 'SDO_GEOMETRY'"
if schema and not self.userTablesOnly:
where = "{} c.owner = {}".format(
- "{} AND".format(where) if where else "WHERE",
- self.quoteString(schema))
+ f"{where} AND" if where else "WHERE", self.quoteString(schema)
+ )
if self.userTablesOnly:
prefix = "user"
@@ -591,15 +629,14 @@ def getVectorTables(self, schema=None):
JOIN {2}_objects o ON c.table_name = o.object_name
AND o.object_type IN ('TABLE','VIEW','SYNONYM') {4} {5}
ORDER BY TABLE_NAME
- """.format(owner,
- "c.srid" if self.geometryColumnsOnly
- else "NULL as srid",
- prefix,
- "sdo_geom_metadata" if self.geometryColumnsOnly
- else "tab_columns",
- "" if self.userTablesOnly
- else "AND c.owner = o.owner",
- where)
+ """.format(
+ owner,
+ "c.srid" if self.geometryColumnsOnly else "NULL as srid",
+ prefix,
+ "sdo_geom_metadata" if self.geometryColumnsOnly else "tab_columns",
+ "" if self.userTablesOnly else "AND c.owner = o.owner",
+ where,
+ )
# For each table, get all of the details
items = []
@@ -617,12 +654,11 @@ def getVectorTables(self, schema=None):
detectedSrid = int(detectedSrid)
if schema:
- table_name = "{}.{}".format(self.quoteId(schema), self.quoteId(item[0]))
+ table_name = f"{self.quoteId(schema)}.{self.quoteId(item[0])}"
else:
table_name = self.quoteId(item[0])
geocol = self.quoteId(item[3])
- geomMultiTypes, multiSrids = self.getTableGeomTypes(
- table_name, geocol)
+ geomMultiTypes, multiSrids = self.getTableGeomTypes(table_name, geocol)
geomtypes = list(geomMultiTypes)
srids = list(multiSrids)
item.insert(0, Table.VectorType)
@@ -632,7 +668,9 @@ def getVectorTables(self, schema=None):
for j in range(len(geomtypes)):
buf = list(item)
geomtype = geomtypes[j]
- datatype = QgsWkbTypes.displayString(QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype)))
+ datatype = QgsWkbTypes.displayString(
+ QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype))
+ )
geo = datatype.upper()
buf.append(geo) # Geometry type as String
buf.append(geomtype) # Qgis.WkbType
@@ -641,8 +679,7 @@ def getVectorTables(self, schema=None):
if not self.onlyExistingTypes:
geomMultiTypes.append(0)
multiSrids.append(multiSrids[0])
- buf.append(",".join([str(x) for x in
- geomMultiTypes]))
+ buf.append(",".join([str(x) for x in geomMultiTypes]))
buf.append(",".join([str(x) for x in multiSrids]))
items.append(buf)
@@ -662,8 +699,7 @@ def getTableComment(self, table, objectType):
schema, tablename = self.getSchemaTableName(table)
data_prefix = "ALL" if schema else "USER"
- where = "AND OWNER = {}".format(
- self.quoteString(schema)) if schema else ""
+ where = f"AND OWNER = {self.quoteString(schema)}" if schema else ""
if objectType in ["TABLE", "VIEW"]:
data_table = "{}_TAB_COMMENTS"
table = "TABLE"
@@ -677,9 +713,9 @@ def getTableComment(self, table, objectType):
sql = """
SELECT COMMENTS FROM {} WHERE {}_NAME = {}
{}
- """.format(data_table, table,
- self.quoteString(tablename),
- where)
+ """.format(
+ data_table, table, self.quoteString(tablename), where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -702,14 +738,13 @@ def getTableType(self, table):
SELECT OBJECT_TYPE FROM {0} WHERE OBJECT_NAME = {1} {2}
"""
if schema:
- sql = sql.format("ALL_OBJECTS",
- self.quoteString(tablename),
- "AND OWNER = {}".format(
- self.quoteString(schema)))
+ sql = sql.format(
+ "ALL_OBJECTS",
+ self.quoteString(tablename),
+ f"AND OWNER = {self.quoteString(schema)}",
+ )
else:
- sql = sql.format("USER_OBJECTS",
- self.quoteString(tablename),
- "")
+ sql = sql.format("USER_OBJECTS", self.quoteString(tablename), "")
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -736,8 +771,10 @@ def pkCols(self, table):
WHERE owner={}
AND table_name={}
ORDER BY column_id
- """.format(self.quoteString(schema) if schema else self.user,
- self.quoteString(tablename))
+ """.format(
+ self.quoteString(schema) if schema else self.user,
+ self.quoteString(tablename),
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
c.close()
@@ -760,7 +797,9 @@ def getTableGeomTypes(self, table, geomCol):
FROM {1} a
WHERE a.{0} IS NOT NULL {2}
ORDER BY a.{0}.SDO_GTYPE
- """.format(geomCol, table, estimated)
+ """.format(
+ geomCol, table, estimated
+ )
try:
c = self._execute(None, query)
@@ -799,12 +838,20 @@ def getTableMainGeomType(self, table, geomCol):
# Make the decision:
wkbType = QgsWkbTypes.Type.Unknown
srid = -1
- order = [QgsWkbTypes.Type.MultiPolygon25D, QgsWkbTypes.Type.Polygon25D,
- QgsWkbTypes.Type.MultiPolygon, QgsWkbTypes.Type.Polygon,
- QgsWkbTypes.Type.MultiLineString25D, QgsWkbTypes.Type.LineString25D,
- QgsWkbTypes.Type.MultiLineString, QgsWkbTypes.Type.LineString,
- QgsWkbTypes.Type.MultiPoint25D, QgsWkbTypes.Type.Point25D,
- QgsWkbTypes.Type.MultiPoint, QgsWkbTypes.Type.Point]
+ order = [
+ QgsWkbTypes.Type.MultiPolygon25D,
+ QgsWkbTypes.Type.Polygon25D,
+ QgsWkbTypes.Type.MultiPolygon,
+ QgsWkbTypes.Type.Polygon,
+ QgsWkbTypes.Type.MultiLineString25D,
+ QgsWkbTypes.Type.LineString25D,
+ QgsWkbTypes.Type.MultiLineString,
+ QgsWkbTypes.Type.LineString,
+ QgsWkbTypes.Type.MultiPoint25D,
+ QgsWkbTypes.Type.Point25D,
+ QgsWkbTypes.Type.MultiPoint,
+ QgsWkbTypes.Type.Point,
+ ]
for geomType in order:
if geomType in geomTypes:
wkbType = geomType
@@ -814,17 +861,18 @@ def getTableMainGeomType(self, table, geomCol):
return wkbType, srid
def getTableRowEstimation(self, table):
- """ Find the estimated number of rows of a table. """
+ """Find the estimated number of rows of a table."""
schema, tablename = self.getSchemaTableName(table)
prefix = "ALL" if schema else "USER"
- where = "AND OWNER = {}".format(
- self.quoteString(schema)) if schema else ""
+ where = f"AND OWNER = {self.quoteString(schema)}" if schema else ""
sql = """
SELECT NUM_ROWS FROM {}_ALL_TABLES
WHERE TABLE_NAME = {}
{}
- """.format(prefix, self.quoteString(tablename), where)
+ """.format(
+ prefix, self.quoteString(tablename), where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -836,17 +884,18 @@ def getTableRowEstimation(self, table):
return int(res[0])
def getTableDates(self, table):
- """ Returns the modification/creation dates of an object"""
+ """Returns the modification/creation dates of an object"""
schema, tablename = self.getSchemaTableName(table)
prefix = "ALL" if schema else "USER"
- where = "AND OWNER = {}".format(
- self.quoteString(schema)) if schema else ""
+ where = f"AND OWNER = {self.quoteString(schema)}" if schema else ""
sql = """
SELECT CREATED, LAST_DDL_TIME FROM {}_OBJECTS
WHERE OBJECT_NAME = {}
{}
- """.format(prefix, self.quoteString(tablename), where)
+ """.format(
+ prefix, self.quoteString(tablename), where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -859,8 +908,7 @@ def getTableDates(self, table):
def getTableRowCount(self, table):
"""Returns the number of rows of the table."""
- c = self._execute(
- None, "SELECT COUNT(*) FROM {}".format(self.quoteId(table)))
+ c = self._execute(None, f"SELECT COUNT(*) FROM {self.quoteId(table)}")
res = self._fetchone(c)[0]
c.close()
@@ -871,7 +919,8 @@ def getTableFields(self, table):
schema, tablename = self.getSchemaTableName(table)
schema_where = " AND a.OWNER={}".format(
- self.quoteString(schema) if schema else "")
+ self.quoteString(schema) if schema else ""
+ )
sql = """
SELECT a.COLUMN_ID As ordinal_position,
a.COLUMN_NAME As column_name,
@@ -892,7 +941,9 @@ def getTableFields(self, table):
AND a.OWNER = c.OWNER
WHERE a.TABLE_NAME = {} {}
ORDER BY a.COLUMN_ID
- """.format(self.quoteString(tablename), schema_where)
+ """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -913,7 +964,8 @@ def getTableIndexes(self, table):
"""Get info about table's indexes."""
schema, tablename = self.getSchemaTableName(table)
schema_where = " AND i.OWNER = {} ".format(
- self.quoteString(schema) if schema else "")
+ self.quoteString(schema) if schema else ""
+ )
sql = """
SELECT i.INDEX_NAME, c.COLUMN_NAME, i.ITYP_NAME,
@@ -922,7 +974,9 @@ def getTableIndexes(self, table):
FROM ALL_INDEXES i
INNER JOIN ALL_IND_COLUMNS c ON i.index_name = c.index_name
WHERE i.table_name = {} {}
- """.format(self.quoteString(tablename), schema_where)
+ """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -933,8 +987,7 @@ def getTableIndexes(self, table):
def getMViewInfo(self, table):
"""Find some information about materialized views"""
schema, tablename = self.getSchemaTableName(table)
- where = " AND a.OWNER = {} ".format(
- self.quoteString(schema)) if schema else ""
+ where = f" AND a.OWNER = {self.quoteString(schema)} " if schema else ""
prefix = "ALL" if schema else "USER"
sql = """
SELECT a.REFRESH_MODE,
@@ -944,7 +997,9 @@ def getMViewInfo(self, table):
FROM {}_MVIEWS a
WHERE MVIEW_NAME = {}
{}
- """.format(prefix, self.quoteString(tablename), where)
+ """.format(
+ prefix, self.quoteString(tablename), where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -955,8 +1010,7 @@ def getMViewInfo(self, table):
def getTableConstraints(self, table):
"""Find all the constraints for a table."""
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND c.OWNER={} ".format(
- self.quoteString(schema)) if schema else ""
+ schema_where = f" AND c.OWNER={self.quoteString(schema)} " if schema else ""
sql = """
SELECT a.CONSTRAINT_NAME, a.CONSTRAINT_TYPE,
@@ -973,7 +1027,9 @@ def getTableConstraints(self, table):
AND a.R_OWNER = b.OWNER
AND b.POSITION = c.POSITION
WHERE c.TABLE_NAME = {} {}
- """.format(self.quoteString(tablename), schema_where)
+ """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -990,7 +1046,9 @@ def getTableTriggers(self, table):
FROM ALL_TRIGGERS
WHERE TABLE_OWNER = {}
AND TABLE_NAME = {}
- """.format(self.quoteString(schema), self.quoteString(tablename))
+ """.format(
+ self.quoteString(schema), self.quoteString(tablename)
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -1015,7 +1073,7 @@ def deleteTableTrigger(self, trigger, table):
"""Deletes the trigger on a table."""
schema, tablename = self.getSchemaTableName(table)
trigger = ".".join([self.quoteId(schema), self.quoteId(trigger)])
- sql = "DROP TRIGGER {}".format(trigger)
+ sql = f"DROP TRIGGER {trigger}"
self._execute_and_commit(sql)
def canUpdateMetadata(self, table):
@@ -1025,12 +1083,13 @@ def canUpdateMetadata(self, table):
schema, tablename = self.getSchemaTableName(table)
metadata = False
# User can only update in USER_SDO_GEOM_METADATA
- if self.getRawTablePrivileges('USER_SDO_GEOM_METADATA', 'MDSYS',
- 'PUBLIC')[2]:
+ if self.getRawTablePrivileges("USER_SDO_GEOM_METADATA", "MDSYS", "PUBLIC")[2]:
tbQuery = """
SELECT COUNT(*) FROM USER_SDO_GEOM_METADATA
WHERE TABLE_NAME = {}
- """.format(self.quoteString(tablename))
+ """.format(
+ self.quoteString(tablename)
+ )
c = self._execute(None, tbQuery)
res = self._fetchone(c)
c.close()
@@ -1044,17 +1103,18 @@ def canUpdateMetadata(self, table):
def getTableExtent(self, table, geom):
"""Calculate the real table extent."""
schema, tablename = self.getSchemaTableName(table)
- tableQuote = "'{}.{}'".format(schema, tablename)
+ tableQuote = f"'{schema}.{tablename}'"
# Extent calculation without spatial index
- extentFunction = """SDO_AGGR_MBR("{}")""".format(geom)
- fromTable = '"{}"."{}"'.format(schema, tablename)
+ extentFunction = f"""SDO_AGGR_MBR("{geom}")"""
+ fromTable = f'"{schema}"."{tablename}"'
# if table as spatial index:
indexes = self.getTableIndexes(table)
if indexes:
if "SPATIAL_INDEX" in [f[2] for f in indexes]:
extentFunction = "SDO_TUNE.EXTENT_OF({}, {})".format(
- tableQuote, self.quoteString(geom))
+ tableQuote, self.quoteString(geom)
+ )
fromTable = "DUAL"
sql = """
@@ -1064,7 +1124,9 @@ def getTableExtent(self, table, geom):
SDO_GEOM.SDO_MAX_MBR_ORDINATE({0}, 1),
SDO_GEOM.SDO_MAX_MBR_ORDINATE({0}, 2)
FROM {1}
- """.format(extentFunction, fromTable)
+ """.format(
+ extentFunction, fromTable
+ )
try:
c = self._execute(None, sql)
@@ -1086,11 +1148,11 @@ def getTableEstimatedExtent(self, table, geom):
where = """
WHERE TABLE_NAME = {}
AND COLUMN_NAME = {}
- """.format(self.quoteString(tablename),
- self.quoteString(geom))
+ """.format(
+ self.quoteString(tablename), self.quoteString(geom)
+ )
if schema:
- where = "{} AND OWNER = {}".format(
- where, self.quoteString(schema))
+ where = f"{where} AND OWNER = {self.quoteString(schema)}"
request = """
SELECT SDO_LB, SDO_UB
@@ -1124,18 +1186,21 @@ def getDefinition(self, view, objectType):
schema, tablename = self.getSchemaTableName(view)
where = ""
if schema:
- where = " AND OWNER={} ".format(
- self.quoteString(schema))
+ where = f" AND OWNER={self.quoteString(schema)} "
# Query to grab a view definition
if objectType == "VIEW":
sql = """
SELECT TEXT FROM ALL_VIEWS WHERE VIEW_NAME = {} {}
- """.format(self.quoteString(tablename), where)
+ """.format(
+ self.quoteString(tablename), where
+ )
elif objectType == "MATERIALIZED VIEW":
sql = """
SELECT QUERY FROM ALL_MVIEWS WHERE MVIEW_NAME = {} {}
- """.format(self.quoteString(tablename), where)
+ """.format(
+ self.quoteString(tablename), where
+ )
else:
return None
@@ -1155,8 +1220,8 @@ def getSpatialRefInfo(self, srid):
try:
c = self._execute(
None,
- ("SELECT CS_NAME FROM MDSYS.CS_SRS WHERE"
- " SRID = {}".format(srid)))
+ ("SELECT CS_NAME FROM MDSYS.CS_SRS WHERE" " SRID = {}".format(srid)),
+ )
except DbError:
return
sr = self._fetchone(c)
@@ -1170,16 +1235,16 @@ def isVectorTable(self, table):
"""
if self.has_geometry_columns and self.has_geometry_columns_access:
schema, tablename = self.getSchemaTableName(table)
- where = "WHERE TABLE_NAME = {}".format(
- self.quoteString(tablename))
+ where = f"WHERE TABLE_NAME = {self.quoteString(tablename)}"
if schema:
- where = "{} AND OWNER = {}".format(where,
- self.quoteString(schema))
+ where = f"{where} AND OWNER = {self.quoteString(schema)}"
sql = """
SELECT COUNT(*)
FROM ALL_SDO_GEOM_METADATA
{}
- """.format(where)
+ """.format(
+ where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -1196,10 +1261,10 @@ def createTable(self, table, field_defs, pkey):
if len(field_defs) == 0:
return False
- sql = "CREATE TABLE {} (".format(self.quoteId(table))
+ sql = f"CREATE TABLE {self.quoteId(table)} ("
sql += ", ".join(field_defs)
if pkey:
- sql += ", PRIMARY KEY ({})".format(self.quoteId(pkey))
+ sql += f", PRIMARY KEY ({self.quoteId(pkey)})"
sql += ")"
self._execute_and_commit(sql)
@@ -1213,13 +1278,13 @@ def deleteTable(self, table):
if self.isVectorTable(table):
self.deleteMetadata(table)
- sql = "DROP TABLE {}".format(self.quoteId(table))
+ sql = f"DROP TABLE {self.quoteId(table)}"
self._execute_and_commit(sql)
def emptyTable(self, table):
"""Deletes all the rows of a table."""
- sql = "TRUNCATE TABLE {}".format(self.quoteId(table))
+ sql = f"TRUNCATE TABLE {self.quoteId(table)}"
self._execute_and_commit(sql)
def renameTable(self, table, new_table):
@@ -1234,15 +1299,14 @@ def renameTable(self, table, new_table):
if self.isVectorTable(table):
self.updateMetadata(table, None, new_table=new_table)
- sql = "RENAME {} TO {}".format(
- self.quoteId(tablename), self.quoteId(new_table))
+ sql = f"RENAME {self.quoteId(tablename)} TO {self.quoteId(new_table)}"
self._execute(c, sql)
self._commit()
def createView(self, view, query):
"""Creates a view as defined."""
- sql = "CREATE VIEW {} AS {}".format(self.quoteId(view), query)
+ sql = f"CREATE VIEW {self.quoteId(view)} AS {query}"
self._execute_and_commit(sql)
def createSpatialView(self, view, query):
@@ -1281,19 +1345,18 @@ def deleteView(self, view):
if self.isVectorTable(view):
self.deleteMetadata(view)
- sql = "DROP VIEW {}".format(self.quoteId(view))
+ sql = f"DROP VIEW {self.quoteId(view)}"
self._execute_and_commit(sql)
def createSchema(self, schema):
"""Creates a new empty schema in database."""
# Not tested
- sql = "CREATE SCHEMA AUTHORIZATION {}".format(
- self.quoteId(schema))
+ sql = f"CREATE SCHEMA AUTHORIZATION {self.quoteId(schema)}"
self._execute_and_commit(sql)
def deleteSchema(self, schema):
"""Drops (empty) schema from database."""
- sql = "DROP USER {} CASCADE".format(self.quoteId(schema))
+ sql = f"DROP USER {self.quoteId(schema)} CASCADE"
self._execute_and_commit(sql)
def renameSchema(self, schema, new_schema):
@@ -1303,14 +1366,13 @@ def renameSchema(self, schema, new_schema):
def addTableColumn(self, table, field_def):
"""Adds a column to a table."""
- sql = "ALTER TABLE {} ADD {}".format(self.quoteId(table), field_def)
+ sql = f"ALTER TABLE {self.quoteId(table)} ADD {field_def}"
self._execute_and_commit(sql)
def deleteTableColumn(self, table, column):
"""Deletes column from a table."""
# Delete all the constraints for this column
- constraints = [f[0] for f in self.getTableConstraints(table)
- if f[2] == column]
+ constraints = [f[0] for f in self.getTableConstraints(table) if f[2] == column]
for constraint in constraints:
self.deleteTableConstraint(table, constraint)
@@ -1324,12 +1386,20 @@ def deleteTableColumn(self, table, column):
self.deleteMetadata(table, column)
sql = "ALTER TABLE {} DROP COLUMN {}".format(
- self.quoteId(table), self.quoteId(column))
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
- def updateTableColumn(self, table, column, new_name=None,
- data_type=None, not_null=None,
- default=None, comment=None):
+ def updateTableColumn(
+ self,
+ table,
+ column,
+ new_name=None,
+ data_type=None,
+ not_null=None,
+ default=None,
+ comment=None,
+ ):
"""Updates properties of a column in a table."""
schema, tablename = self.getSchemaTableName(table)
@@ -1339,9 +1409,9 @@ def updateTableColumn(self, table, column, new_name=None,
# update column definition
col_actions = []
if data_type:
- col_actions.append("{}".format(data_type))
+ col_actions.append(f"{data_type}")
if default:
- col_actions.append("DEFAULT {}".format(default))
+ col_actions.append(f"DEFAULT {default}")
else:
col_actions.append("DEFAULT NULL")
@@ -1352,16 +1422,16 @@ def updateTableColumn(self, table, column, new_name=None,
if col_actions:
sql = "ALTER TABLE {} MODIFY ( {} {} )".format(
- self.quoteId(table), self.quoteId(column),
- " ".join(col_actions))
+ self.quoteId(table), self.quoteId(column), " ".join(col_actions)
+ )
self._execute(c, sql)
# rename the column
if new_name and new_name != column:
isGeo = self.isGeometryColumn(table, column)
sql = "ALTER TABLE {} RENAME COLUMN {} TO {}".format(
- self.quoteId(table), self.quoteId(column),
- self.quoteId(new_name))
+ self.quoteId(table), self.quoteId(column), self.quoteId(new_name)
+ )
self._execute(c, sql)
# update geometry_columns if Spatial is enabled
@@ -1392,16 +1462,16 @@ def isGeometryColumn(self, table, column):
"""Find if a column is geometric."""
schema, tablename = self.getSchemaTableName(table)
prefix = "ALL" if schema else "USER"
- where = "AND owner = {} ".format(
- self.quoteString(schema)) if schema else ""
+ where = f"AND owner = {self.quoteString(schema)} " if schema else ""
sql = """
SELECT COUNT(*)
FROM {}_SDO_GEOM_METADATA
WHERE TABLE_NAME = {}
AND COLUMN_NAME = {} {}
- """.format(prefix, self.quoteString(tablename),
- self.quoteString(column.upper()), where)
+ """.format(
+ prefix, self.quoteString(tablename), self.quoteString(column.upper()), where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)[0] > 0
@@ -1412,92 +1482,100 @@ def isGeometryColumn(self, table, column):
def refreshMView(self, table):
"""Refreshes an MVIEW"""
schema, tablename = self.getSchemaTableName(table)
- mview = "{}.{}".format(schema, tablename) if schema else tablename
+ mview = f"{schema}.{tablename}" if schema else tablename
sql = """
BEGIN
DBMS_MVIEW.REFRESH({},'?');
END;
- """.format(self.quoteString(mview))
+ """.format(
+ self.quoteString(mview)
+ )
self._execute_and_commit(sql)
def deleteMetadata(self, table, geom_column=None):
"""Deletes the metadata entry for a table"""
schema, tablename = self.getSchemaTableName(table)
- if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
- 'MDSYS',
- 'PUBLIC')[3] and
- schema == self.user):
+ if not (
+ self.getRawTablePrivileges("USER_SDO_GEOM_METADATA", "MDSYS", "PUBLIC")[3]
+ and schema == self.user
+ ):
return False
- where = "WHERE TABLE_NAME = {}".format(self.quoteString(tablename))
+ where = f"WHERE TABLE_NAME = {self.quoteString(tablename)}"
if geom_column:
- where = ("{} AND COLUMN_NAME = "
- "{}".format(where,
- self.quoteString(geom_column)))
- sql = "DELETE FROM USER_SDO_GEOM_METADATA {}".format(where)
+ where = "{} AND COLUMN_NAME = " "{}".format(
+ where, self.quoteString(geom_column)
+ )
+ sql = f"DELETE FROM USER_SDO_GEOM_METADATA {where}"
self._execute_and_commit(sql)
- def updateMetadata(self, table, geom_column, new_geom_column=None,
- new_table=None, extent=None, srid=None):
+ def updateMetadata(
+ self,
+ table,
+ geom_column,
+ new_geom_column=None,
+ new_table=None,
+ extent=None,
+ srid=None,
+ ):
"""Updates the metadata table with the new information"""
schema, tablename = self.getSchemaTableName(table)
- if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
- 'MDSYS',
- 'PUBLIC')[2] and
- schema == self.user):
+ if not (
+ self.getRawTablePrivileges("USER_SDO_GEOM_METADATA", "MDSYS", "PUBLIC")[2]
+ and schema == self.user
+ ):
return False
- where = "WHERE TABLE_NAME = {}".format(self.quoteString(tablename))
+ where = f"WHERE TABLE_NAME = {self.quoteString(tablename)}"
if geom_column:
# in Metadata view, geographic column is always in uppercase
- where = ("{} AND COLUMN_NAME = "
- "{}".format(where,
- self.quoteString(geom_column.upper())))
+ where = "{} AND COLUMN_NAME = " "{}".format(
+ where, self.quoteString(geom_column.upper())
+ )
update = "SET"
if srid == 0:
srid = -1
if srid:
- update = "{} SRID = {}".format(update, srid)
+ update = f"{update} SRID = {srid}"
if extent:
if len(extent) == 4:
if update != "SET":
- update = "{},".format(update)
+ update = f"{update},"
update = """{4} DIMINFO = MDSYS.SDO_DIM_ARRAY(
MDSYS.SDO_DIM_ELEMENT('X', {0:.9f}, {1:.9f}, 0.005),
MDSYS.SDO_DIM_ELEMENT('Y', {2:.9f}, {3:.9f}, 0.005))
- """.format(extent[0], extent[2], extent[1],
- extent[3], update)
+ """.format(
+ extent[0], extent[2], extent[1], extent[3], update
+ )
if new_geom_column:
if update != "SET":
- update = "{},".format(update)
+ update = f"{update},"
# in Metadata view, geographic column is always in uppercase
- update = ("{} COLUMN_NAME = "
- "{}".format(update,
- self.quoteString(new_geom_column.upper())))
+ update = "{} COLUMN_NAME = " "{}".format(
+ update, self.quoteString(new_geom_column.upper())
+ )
if new_table:
if update != "SET":
- update = "{},".format(update)
- update = ("{} TABLE_NAME = "
- "{}".format(update,
- self.quoteString(new_table)))
+ update = f"{update},"
+ update = "{} TABLE_NAME = " "{}".format(update, self.quoteString(new_table))
- sql = "UPDATE USER_SDO_GEOM_METADATA {} {}".format(update, where)
+ sql = f"UPDATE USER_SDO_GEOM_METADATA {update} {where}"
self._execute_and_commit(sql)
def insertMetadata(self, table, geom_column, extent, srid, dim=2):
"""Inserts a line for the table in Oracle Metadata table."""
schema, tablename = self.getSchemaTableName(table)
- if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
- 'MDSYS',
- 'PUBLIC')[1] and
- schema == self.user):
+ if not (
+ self.getRawTablePrivileges("USER_SDO_GEOM_METADATA", "MDSYS", "PUBLIC")[1]
+ and schema == self.user
+ ):
return False
# in Metadata view, geographic column is always in uppercase
@@ -1507,16 +1585,21 @@ def insertMetadata(self, table, geom_column, extent, srid, dim=2):
if len(extent) != 4:
return False
- dims = ['X', 'Y', 'Z', 'T']
+ dims = ["X", "Y", "Z", "T"]
extentParts = []
for i in range(dim):
extentParts.append(
"""MDSYS.SDO_DIM_ELEMENT(
- '{}', {:.9f}, {:.9f}, 0.005)""".format(dims[i], extent[i], extent[i + 1]))
+ '{}', {:.9f}, {:.9f}, 0.005)""".format(
+ dims[i], extent[i], extent[i + 1]
+ )
+ )
extentParts = ",".join(extentParts)
sqlExtent = """MDSYS.SDO_DIM_ARRAY(
{})
- """.format(extentParts)
+ """.format(
+ extentParts
+ )
sql = """
INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME,
@@ -1525,14 +1608,18 @@ def insertMetadata(self, table, geom_column, extent, srid, dim=2):
VALUES({}, {},
{},
{})
- """.format(self.quoteString(tablename),
- self.quoteString(geom_column),
- sqlExtent, str(srid))
+ """.format(
+ self.quoteString(tablename),
+ self.quoteString(geom_column),
+ sqlExtent,
+ str(srid),
+ )
self._execute_and_commit(sql)
- def addGeometryColumn(self, table, geom_column='GEOM',
- geom_type=None, srid=-1, dim=2):
+ def addGeometryColumn(
+ self, table, geom_column="GEOM", geom_type=None, srid=-1, dim=2
+ ):
"""Adds a geometry column and update Oracle Spatial
metadata.
"""
@@ -1543,7 +1630,8 @@ def addGeometryColumn(self, table, geom_column='GEOM',
# Add the column to the table
sql = "ALTER TABLE {} ADD {} SDO_GEOMETRY".format(
- self.quoteId(table), self.quoteId(geom_column))
+ self.quoteId(table), self.quoteId(geom_column)
+ )
self._execute_and_commit(sql)
@@ -1551,9 +1639,9 @@ def addGeometryColumn(self, table, geom_column='GEOM',
extent = []
for i in range(dim):
extent.extend([-100000, 10000])
- self.insertMetadata(table, geom_column,
- [-100000, 100000, -10000, 10000],
- srid, dim)
+ self.insertMetadata(
+ table, geom_column, [-100000, 100000, -10000, 10000], srid, dim
+ )
def deleteGeometryColumn(self, table, geom_column):
"""Deletes a geometric column."""
@@ -1562,57 +1650,61 @@ def deleteGeometryColumn(self, table, geom_column):
def addTableUniqueConstraint(self, table, column):
"""Adds a unique constraint to a table."""
sql = "ALTER TABLE {} ADD UNIQUE ({})".format(
- self.quoteId(table), self.quoteId(column))
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def deleteTableConstraint(self, table, constraint):
"""Deletes constraint in a table."""
sql = "ALTER TABLE {} DROP CONSTRAINT {}".format(
- self.quoteId(table), self.quoteId(constraint))
+ self.quoteId(table), self.quoteId(constraint)
+ )
self._execute_and_commit(sql)
def addTablePrimaryKey(self, table, column):
"""Adds a primary key (with one column) to a table."""
sql = "ALTER TABLE {} ADD PRIMARY KEY ({})".format(
- self.quoteId(table), self.quoteId(column))
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def createTableIndex(self, table, name, column):
"""Creates index on one column using default options."""
sql = "CREATE INDEX {} ON {} ({})".format(
- self.quoteId(name), self.quoteId(table),
- self.quoteId(column))
+ self.quoteId(name), self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def rebuildTableIndex(self, table, name):
"""Rebuilds a table index"""
schema, tablename = self.getSchemaTableName(table)
- sql = "ALTER INDEX {} REBUILD".format(self.quoteId((schema, name)))
+ sql = f"ALTER INDEX {self.quoteId((schema, name))} REBUILD"
self._execute_and_commit(sql)
def deleteTableIndex(self, table, name):
"""Deletes an index on a table."""
schema, tablename = self.getSchemaTableName(table)
- sql = "DROP INDEX {}".format(self.quoteId((schema, name)))
+ sql = f"DROP INDEX {self.quoteId((schema, name))}"
self._execute_and_commit(sql)
- def createSpatialIndex(self, table, geom_column='GEOM'):
+ def createSpatialIndex(self, table, geom_column="GEOM"):
"""Creates a spatial index on a geometric column."""
geom_column = geom_column.upper()
schema, tablename = self.getSchemaTableName(table)
- idx_name = self.quoteId("sidx_{}_{}".format(tablename, geom_column))
+ idx_name = self.quoteId(f"sidx_{tablename}_{geom_column}")
sql = """
CREATE INDEX {}
ON {}({})
INDEXTYPE IS MDSYS.SPATIAL_INDEX
- """.format(idx_name, self.quoteId(table),
- self.quoteId(geom_column))
+ """.format(
+ idx_name, self.quoteId(table), self.quoteId(geom_column)
+ )
self._execute_and_commit(sql)
- def deleteSpatialIndex(self, table, geom_column='GEOM'):
+ def deleteSpatialIndex(self, table, geom_column="GEOM"):
"""Deletes a spatial index of a geometric column."""
schema, tablename = self.getSchemaTableName(table)
- idx_name = self.quoteId("sidx_{}_{}".format(tablename, geom_column))
+ idx_name = self.quoteId(f"sidx_{tablename}_{geom_column}")
return self.deleteTableIndex(table, idx_name)
def execution_error_types(self):
@@ -1672,6 +1764,7 @@ def _close_cursor(self, c):
def getSqlDictionary(self):
"""Returns the dictionary for SQL dialog."""
from .sql_dictionary import getSqlDictionary
+
sql_dict = getSqlDictionary()
# get schemas, tables and field names
@@ -1683,7 +1776,9 @@ def getSqlDictionary(self):
SELECT DISTINCT tablename FROM "oracle_{0}"
UNION
SELECT DISTINCT ownername FROM "oracle_{0}"
- """.format(self.connName)
+ """.format(
+ self.connName
+ )
if self.userTablesOnly:
sql = """
SELECT DISTINCT tablename
@@ -1691,7 +1786,9 @@ def getSqlDictionary(self):
UNION
SELECT DISTINCT ownername
FROM "oracle_{conn}" WHERE ownername = '{user}'
- """.format(conn=self.connName, user=self.user)
+ """.format(
+ conn=self.connName, user=self.user
+ )
c = self.cache_connection.cursor()
c.execute(sql)
@@ -1702,8 +1799,9 @@ def getSqlDictionary(self):
if self.hasCache():
sql = """
SELECT DISTINCT COLUMN_NAME FROM {}_TAB_COLUMNS
- """.format("USER" if self.userTablesOnly else
- "ALL")
+ """.format(
+ "USER" if self.userTablesOnly else "ALL"
+ )
elif self.userTablesOnly:
sql = """
SELECT DISTINCT TABLE_NAME FROM USER_ALL_TABLES
@@ -1731,6 +1829,7 @@ def getSqlDictionary(self):
def getQueryBuilderDictionary(self):
from .sql_dictionary import getQueryBuilderDictionary
+
return getQueryBuilderDictionary()
def cancel(self):
diff --git a/python/plugins/db_manager/db_plugins/oracle/data_model.py b/python/plugins/db_manager/db_plugins/oracle/data_model.py
index 0fc8f6512932..f86336c2154a 100644
--- a/python/plugins/db_manager/db_plugins/oracle/data_model.py
+++ b/python/plugins/db_manager/db_plugins/oracle/data_model.py
@@ -23,11 +23,13 @@
from qgis.PyQt.QtCore import QElapsedTimer
from qgis.core import QgsMessageLog
-from ..data_model import (TableDataModel,
- SqlResultModel,
- SqlResultModelAsync,
- SqlResultModelTask,
- BaseTableModel)
+from ..data_model import (
+ TableDataModel,
+ SqlResultModel,
+ SqlResultModelAsync,
+ SqlResultModelTask,
+ BaseTableModel,
+)
from ..plugin import DbError
from ..plugin import BaseError
@@ -46,39 +48,38 @@ def __init__(self, table, parent=None):
def _createCursor(self):
fields_txt = ", ".join(self.fields)
- table_txt = self.db.quoteId(
- (self.table.schemaName(), self.table.name))
+ table_txt = self.db.quoteId((self.table.schemaName(), self.table.name))
self.cursor = self.db._get_cursor()
- sql = "SELECT {} FROM {}".format(fields_txt, table_txt)
+ sql = f"SELECT {fields_txt} FROM {table_txt}"
self.db._execute(self.cursor, sql)
def _sanitizeTableField(self, field):
# get fields, ignore geometry columns
if field.dataType.upper() == "SDO_GEOMETRY":
- return ("CASE WHEN {0} IS NULL THEN NULL ELSE 'GEOMETRY'"
- "END AS {0}".format(
- self.db.quoteId(field.name)))
+ return (
+ "CASE WHEN {0} IS NULL THEN NULL ELSE 'GEOMETRY'"
+ "END AS {0}".format(self.db.quoteId(field.name))
+ )
if field.dataType.upper() == "DATE":
- return "CAST({} AS VARCHAR2(8))".format(
- self.db.quoteId(field.name))
+ return f"CAST({self.db.quoteId(field.name)} AS VARCHAR2(8))"
if "TIMESTAMP" in field.dataType.upper():
return "TO_CHAR({}, 'YYYY-MM-DD HH:MI:SS.FF')".format(
- self.db.quoteId(field.name))
+ self.db.quoteId(field.name)
+ )
if field.dataType.upper() == "NUMBER":
if not field.charMaxLen:
- return "CAST({} AS VARCHAR2(135))".format(
- self.db.quoteId(field.name))
+ return f"CAST({self.db.quoteId(field.name)} AS VARCHAR2(135))"
elif field.modifier:
- nbChars = 2 + int(field.charMaxLen) + \
- int(field.modifier)
+ nbChars = 2 + int(field.charMaxLen) + int(field.modifier)
return "CAST({} AS VARCHAR2({}))".format(
- self.db.quoteId(field.name),
- str(nbChars))
+ self.db.quoteId(field.name), str(nbChars)
+ )
return "CAST({} As VARCHAR2({}))".format(
- self.db.quoteId(field.name), field.charMaxLen)
+ self.db.quoteId(field.name), field.charMaxLen
+ )
def _deleteCursor(self):
self.db._close_cursor(self.cursor)
@@ -89,8 +90,7 @@ def __del__(self):
self._deleteCursor()
def getData(self, row, col):
- if (row < self.fetchedFrom or
- row >= self.fetchedFrom + self.fetchedCount):
+ if row < self.fetchedFrom or row >= self.fetchedFrom + self.fetchedCount:
margin = self.fetchedCount / 2
if row + margin >= self.rowCount():
start = int(self.rowCount() - margin)
diff --git a/python/plugins/db_manager/db_plugins/oracle/info_model.py b/python/plugins/db_manager/db_plugins/oracle/info_model.py
index c8d531ab06eb..08805225e07a 100644
--- a/python/plugins/db_manager/db_plugins/oracle/info_model.py
+++ b/python/plugins/db_manager/db_plugins/oracle/info_model.py
@@ -25,8 +25,14 @@
from qgis.core import QgsWkbTypes
from ..info_model import TableInfo, VectorTableInfo, DatabaseInfo
-from ..html_elems import HtmlContent, HtmlSection, HtmlParagraph, \
- HtmlTable, HtmlTableHeader, HtmlTableCol
+from ..html_elems import (
+ HtmlContent,
+ HtmlSection,
+ HtmlParagraph,
+ HtmlTable,
+ HtmlTableHeader,
+ HtmlTableCol,
+)
# Syntax Highlight for VIEWS/MVIEWS
from pygments import highlight
@@ -43,16 +49,27 @@ def connectionDetails(self):
tbl = []
if self.db.connector.host != "":
- tbl.append((QApplication.translate("DBManagerPlugin", "Host:"),
- self.db.connector.host))
- tbl.append((QApplication.translate("DBManagerPlugin", "Database:"),
- self.db.connector.dbname))
- tbl.append((QApplication.translate("DBManagerPlugin", "User:"),
- self.db.connector.user))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "SQLite list tables cache:"),
- "Enabled" if self.db.connector.hasCache else
- "Unavailable"))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Host:"),
+ self.db.connector.host,
+ )
+ )
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Database:"),
+ self.db.connector.dbname,
+ )
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "User:"), self.db.connector.user)
+ )
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "SQLite list tables cache:"),
+ "Enabled" if self.db.connector.hasCache else "Unavailable",
+ )
+ )
return HtmlTable(tbl)
@@ -63,10 +80,7 @@ def spatialInfo(self):
if not info:
return
- tbl = [
- (QApplication.translate("DBManagerPlugin", "Oracle Spatial:"),
- info[0])
- ]
+ tbl = [(QApplication.translate("DBManagerPlugin", "Oracle Spatial:"), info[0])]
ret.append(HtmlTable(tbl))
if not self.db.connector.has_geometry_columns:
@@ -77,12 +91,15 @@ def spatialInfo(self):
" ALL_SDO_GEOM_METADATA"
" view doesn't exist!\n"
"This view is essential for many"
- " GIS applications for enumeration of tables.")))
+ " GIS applications for enumeration of tables.",
+ )
+ )
+ )
return ret
def privilegesDetails(self):
- """ find if user can create schemas (CREATE ANY TABLE or something)"""
+ """find if user can create schemas (CREATE ANY TABLE or something)"""
# TODO
return None
@@ -105,9 +122,11 @@ def generalInfo(self):
# if the estimation is less than 100 rows, try to count them - it
# shouldn't take long time
- if (not self.table.isView and
- not self.table.rowCount and
- self.table.estimatedRowCount < 100):
+ if (
+ not self.table.isView
+ and not self.table.rowCount
+ and self.table.estimatedRowCount < 100
+ ):
# row count information is not displayed yet, so just block
# table signals to avoid double refreshing
# (infoViewer->refreshRowCount->tableChanged->infoViewer)
@@ -115,70 +134,92 @@ def generalInfo(self):
self.table.refreshRowCount()
self.table.blockSignals(False)
- relation_type = QApplication.translate(
- "DBManagerPlugin", self.table.objectType) if isinstance(self.table.objectType, str) else QApplication.translate("DBManagerPlugin", "Unknown")
+ relation_type = (
+ QApplication.translate("DBManagerPlugin", self.table.objectType)
+ if isinstance(self.table.objectType, str)
+ else QApplication.translate("DBManagerPlugin", "Unknown")
+ )
tbl = [
- (QApplication.translate("DBManagerPlugin", "Object type:"),
- relation_type),
- (QApplication.translate("DBManagerPlugin", "Owner:"),
- self.table.owner)
+ (QApplication.translate("DBManagerPlugin", "Object type:"), relation_type),
+ (QApplication.translate("DBManagerPlugin", "Owner:"), self.table.owner),
]
if self.table.comment:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin",
- "Comment:"),
- self.table.comment))
+ (
+ QApplication.translate("DBManagerPlugin", "Comment:"),
+ self.table.comment,
+ )
+ )
# Estimated rows
if not self.table.isView:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Rows (estimation):"),
- self.table.estimatedRowCount)
+ (
+ QApplication.translate("DBManagerPlugin", "Rows (estimation):"),
+ self.table.estimatedRowCount,
+ )
)
if self.table.rowCount is not None and self.table.rowCount >= 0:
# Add a real count of rows
tbl.append(
- (QApplication.translate("DBManagerPlugin", "Rows (counted):"),
- self.table.rowCount)
+ (
+ QApplication.translate("DBManagerPlugin", "Rows (counted):"),
+ self.table.rowCount,
+ )
)
else:
tbl.append(
- (QApplication.translate("DBManagerPlugin", "Rows (counted):"),
- 'Unknown (find out)')
+ (
+ QApplication.translate("DBManagerPlugin", "Rows (counted):"),
+ 'Unknown (find out)',
+ )
)
# Add creation and modification dates
if self.table.creationDate:
tbl.append(
- (QApplication.translate("DBManagerPlugin", "Creation Date:"),
- self.table.creationDate))
+ (
+ QApplication.translate("DBManagerPlugin", "Creation Date:"),
+ self.table.creationDate,
+ )
+ )
if self.table.modificationDate:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Last Modification Date:"),
- self.table.modificationDate))
+ (
+ QApplication.translate(
+ "DBManagerPlugin", "Last Modification Date:"
+ ),
+ self.table.modificationDate,
+ )
+ )
# privileges
# has the user access to this schema?
- schema_priv = self.table.database().connector.getSchemaPrivileges(
- self.table.schemaName()) if self.table.schema() else None
+ schema_priv = (
+ self.table.database().connector.getSchemaPrivileges(self.table.schemaName())
+ if self.table.schema()
+ else None
+ )
if not schema_priv:
pass
elif schema_priv[1] is False: # no usage privileges on the schema
- tbl.append((QApplication.translate(
- "DBManagerPlugin", "Privileges:"),
- QApplication.translate(
- "DBManagerPlugin",
- " This user doesn't have usage privileges"
- " for this schema!")))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Privileges:"),
+ QApplication.translate(
+ "DBManagerPlugin",
+ " This user doesn't have usage privileges"
+ " for this schema!",
+ ),
+ )
+ )
else:
table_priv = self.table.database().connector.getTablePrivileges(
- (self.table.schemaName(), self.table.name))
+ (self.table.schemaName(), self.table.name)
+ )
privileges = []
if table_priv[0]:
privileges.append("select")
@@ -193,35 +234,43 @@ def generalInfo(self):
priv_string = ", ".join(privileges)
else:
priv_string = QApplication.translate(
- "DBManagerPlugin",
- ' This user has no privileges!')
+ "DBManagerPlugin", " This user has no privileges!"
+ )
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Privileges:"),
- priv_string))
+ (QApplication.translate("DBManagerPlugin", "Privileges:"), priv_string)
+ )
ret.append(HtmlTable(tbl))
if schema_priv and schema_priv[1]:
- if (table_priv[0] and
- not table_priv[1] and
- not table_priv[2] and
- not table_priv[3]):
+ if (
+ table_priv[0]
+ and not table_priv[1]
+ and not table_priv[2]
+ and not table_priv[3]
+ ):
ret.append(
- HtmlParagraph(QApplication.translate(
- "DBManagerPlugin",
- " This user has read-only privileges.")))
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " This user has read-only privileges.",
+ )
+ )
+ )
# primary key defined?
- if (not self.table.isView and
- self.table.objectType != "MATERIALIZED VIEW"):
+ if not self.table.isView and self.table.objectType != "MATERIALIZED VIEW":
pk = [fld for fld in self.table.fields() if fld.primaryKey]
if len(pk) <= 0:
ret.append(
- HtmlParagraph(QApplication.translate(
- "DBManagerPlugin",
- " No primary key defined for this table!")))
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " No primary key defined for this table!",
+ )
+ )
+ )
return ret
@@ -232,19 +281,20 @@ def getSpatialInfo(self):
if not info:
return
- tbl = [
- (QApplication.translate(
- "DBManagerPlugin", "Library:"), info[0]) # ,
- ]
+ tbl = [(QApplication.translate("DBManagerPlugin", "Library:"), info[0])] # ,
ret.append(HtmlTable(tbl))
if not self.db.connector.has_geometry_columns:
- ret.append(HtmlParagraph(
- QApplication.translate(
- "DBManagerPlugin",
- " ALL_SDO_GEOM_METADATA table doesn't exist!\n"
- "This table is essential for many GIS"
- " applications for enumeration of tables.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " ALL_SDO_GEOM_METADATA table doesn't exist!\n"
+ "This table is essential for many GIS"
+ " applications for enumeration of tables.",
+ )
+ )
+ )
return ret
@@ -259,14 +309,15 @@ def fieldsDetails(self):
QApplication.translate("DBManagerPlugin", "Length"),
QApplication.translate("DBManagerPlugin", "Null"),
QApplication.translate("DBManagerPlugin", "Default"),
- QApplication.translate("DBManagerPlugin", "Comment"))
+ QApplication.translate("DBManagerPlugin", "Comment"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for fld in self.table.fields():
char_max_len = fld.charMaxLen if fld.charMaxLen else ""
if fld.modifier:
- char_max_len = "{},{}".format(char_max_len, fld.modifier)
+ char_max_len = f"{char_max_len},{fld.modifier}"
is_null_txt = "N" if fld.notNull else "Y"
# make primary key field underlined
@@ -274,8 +325,16 @@ def fieldsDetails(self):
name = HtmlTableCol(fld.name, attrs)
tbl.append(
- (fld.num, name, fld.type2String(), char_max_len,
- is_null_txt, fld.default2String(), fld.comment))
+ (
+ fld.num,
+ name,
+ fld.type2String(),
+ char_max_len,
+ is_null_txt,
+ fld.default2String(),
+ fld.comment,
+ )
+ )
return HtmlTable(tbl, {"class": "header"})
@@ -286,24 +345,36 @@ def constraintsDetails(self):
tbl = []
# define the table header
- header = (QApplication.translate("DBManagerPlugin", "Name"),
- QApplication.translate("DBManagerPlugin", "Type"),
- QApplication.translate("DBManagerPlugin", "Column"),
- QApplication.translate("DBManagerPlugin", "Status"),
- QApplication.translate("DBManagerPlugin", "Validated"),
- QApplication.translate("DBManagerPlugin", "Generated"),
- QApplication.translate("DBManagerPlugin", "Check condition"),
- QApplication.translate("DBManagerPlugin", "Foreign Table"),
- QApplication.translate("DBManagerPlugin", "Foreign column"),
- QApplication.translate("DBManagerPlugin", "On Delete"))
+ header = (
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Column"),
+ QApplication.translate("DBManagerPlugin", "Status"),
+ QApplication.translate("DBManagerPlugin", "Validated"),
+ QApplication.translate("DBManagerPlugin", "Generated"),
+ QApplication.translate("DBManagerPlugin", "Check condition"),
+ QApplication.translate("DBManagerPlugin", "Foreign Table"),
+ QApplication.translate("DBManagerPlugin", "Foreign column"),
+ QApplication.translate("DBManagerPlugin", "On Delete"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for con in self.table.constraints():
- tbl.append((con.name, con.type2String(), con.column,
- con.status, con.validated, con.generated,
- con.checkSource, con.foreignTable,
- con.foreignKey, con.foreignOnDelete))
+ tbl.append(
+ (
+ con.name,
+ con.type2String(),
+ con.column,
+ con.status,
+ con.validated,
+ con.generated,
+ con.checkSource,
+ con.foreignTable,
+ con.foreignKey,
+ con.foreignOnDelete,
+ )
+ )
return HtmlTable(tbl, {"class": "header"})
@@ -314,24 +385,36 @@ def indexesDetails(self):
tbl = []
# define the table header
- header = (QApplication.translate("DBManagerPlugin", "Name"),
- QApplication.translate("DBManagerPlugin", "Column(s)"),
- QApplication.translate("DBManagerPlugin", "Index Type"),
- QApplication.translate("DBManagerPlugin", "Status"),
- QApplication.translate("DBManagerPlugin", "Last analyzed"),
- QApplication.translate("DBManagerPlugin", "Compression"),
- QApplication.translate("DBManagerPlugin", "Uniqueness"),
- QApplication.translate("DBManagerPlugin", "Action"))
+ header = (
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Column(s)"),
+ QApplication.translate("DBManagerPlugin", "Index Type"),
+ QApplication.translate("DBManagerPlugin", "Status"),
+ QApplication.translate("DBManagerPlugin", "Last analyzed"),
+ QApplication.translate("DBManagerPlugin", "Compression"),
+ QApplication.translate("DBManagerPlugin", "Uniqueness"),
+ QApplication.translate("DBManagerPlugin", "Action"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for idx in self.table.indexes():
# get the fields the index is defined on
- tbl.append((idx.name, idx.column, idx.indexType,
- idx.status, idx.analyzed, idx.compression,
- idx.isUnique,
- ('Rebuild'
- """""".format(idx.name))))
+ tbl.append(
+ (
+ idx.name,
+ idx.column,
+ idx.indexType,
+ idx.status,
+ idx.analyzed,
+ idx.compression,
+ idx.isUnique,
+ (
+ 'Rebuild'
+ """""".format(idx.name)
+ ),
+ )
+ )
return HtmlTable(tbl, {"class": "header"})
@@ -347,26 +430,31 @@ def triggersDetails(self):
QApplication.translate("DBManagerPlugin", "Name"),
QApplication.translate("DBManagerPlugin", "Event"),
QApplication.translate("DBManagerPlugin", "Type"),
- QApplication.translate("DBManagerPlugin", "Enabled"))
+ QApplication.translate("DBManagerPlugin", "Enabled"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for trig in self.table.triggers():
- name = ("""{0} ({1})""".format(trig.name, "delete"))
+ name = """{0} ({1})""".format(
+ trig.name, "delete"
+ )
if trig.enabled == "ENABLED":
enabled, action = (
QApplication.translate("DBManagerPlugin", "Yes"),
- "disable")
+ "disable",
+ )
else:
enabled, action = (
QApplication.translate("DBManagerPlugin", "No"),
- "enable")
+ "enable",
+ )
- txt_enabled = ("""{0} ({2})""".format(
- enabled, trig.name, action))
+ txt_enabled = (
+ """{0} ({2})""".format(enabled, trig.name, action)
+ )
tbl.append((name, trig.event, trig.type, txt_enabled))
@@ -377,9 +465,12 @@ def triggersDetails(self):
QApplication.translate(
"DBManagerPlugin",
''
- 'Enable all triggers / '
+ "Enable all triggers / "
''
- 'Disable all triggers')))
+ "Disable all triggers",
+ )
+ )
+ )
return ret
@@ -392,9 +483,10 @@ def getTableInfo(self):
else:
ret.append(
HtmlSection(
- QApplication.translate(
- "DBManagerPlugin", 'General info'),
- general_info))
+ QApplication.translate("DBManagerPlugin", "General info"),
+ general_info,
+ )
+ )
# spatial info
spatial_info = self.spatialInfo()
@@ -404,12 +496,13 @@ def getTableInfo(self):
spatial_info = HtmlContent(spatial_info)
if not spatial_info.hasContents():
spatial_info = QApplication.translate(
- "DBManagerPlugin",
- ' This is not a spatial table.')
+ "DBManagerPlugin", " This is not a spatial table."
+ )
ret.append(
HtmlSection(
- self.table.database().connection().typeNameString(),
- spatial_info))
+ self.table.database().connection().typeNameString(), spatial_info
+ )
+ )
# fields
fields_details = self.fieldsDetails()
@@ -418,10 +511,9 @@ def getTableInfo(self):
else:
ret.append(
HtmlSection(
- QApplication.translate(
- "DBManagerPlugin",
- 'Fields'),
- fields_details))
+ QApplication.translate("DBManagerPlugin", "Fields"), fields_details
+ )
+ )
# constraints
constraints_details = self.constraintsDetails()
@@ -430,10 +522,10 @@ def getTableInfo(self):
else:
ret.append(
HtmlSection(
- QApplication.translate(
- "DBManagerPlugin",
- 'Constraints'),
- constraints_details))
+ QApplication.translate("DBManagerPlugin", "Constraints"),
+ constraints_details,
+ )
+ )
# indexes
indexes_details = self.indexesDetails()
@@ -442,10 +534,10 @@ def getTableInfo(self):
else:
ret.append(
HtmlSection(
- QApplication.translate(
- "DBManagerPlugin",
- 'Indexes'),
- indexes_details))
+ QApplication.translate("DBManagerPlugin", "Indexes"),
+ indexes_details,
+ )
+ )
# triggers
triggers_details = self.triggersDetails()
@@ -454,19 +546,21 @@ def getTableInfo(self):
else:
ret.append(
HtmlSection(
- QApplication.translate(
- "DBManagerPlugin",
- 'Triggers'),
- triggers_details))
+ QApplication.translate("DBManagerPlugin", "Triggers"),
+ triggers_details,
+ )
+ )
if self.table.objectType == "MATERIALIZED VIEW":
mview_info = self.getMViewInfo()
ret.append(
HtmlSection(
QApplication.translate(
- "DBManagerPlugin",
- 'Materialized View information'),
- mview_info))
+ "DBManagerPlugin", "Materialized View information"
+ ),
+ mview_info,
+ )
+ )
return ret
@@ -477,39 +571,44 @@ def getMViewInfo(self):
ret = []
tbl = []
values = self.table.getMViewInfo()
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Refresh Mode:"),
- values[0]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Refresh Method:"),
- values[1]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Build Mode:"),
- values[2]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Last Refresh Date:"),
- values[5]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Last Refresh Type:"),
- values[4]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Fast Refreshable:"),
- values[3]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Staleness:"),
- values[6]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Stale since:"),
- values[7]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Compile State:"),
- values[8]))
- tbl.append((QApplication.translate("DBManagerPlugin",
- "Use no index:"),
- values[9]))
- tbl.append(('{}'.format(
- QApplication.translate("DBManagerPlugin", "Refresh the materialized view")),
- ""))
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Refresh Mode:"), values[0])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Refresh Method:"), values[1])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Build Mode:"), values[2])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Last Refresh Date:"), values[5])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Last Refresh Type:"), values[4])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Fast Refreshable:"), values[3])
+ )
+ tbl.append((QApplication.translate("DBManagerPlugin", "Staleness:"), values[6]))
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Stale since:"), values[7])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Compile State:"), values[8])
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Use no index:"), values[9])
+ )
+ tbl.append(
+ (
+ '{}'.format(
+ QApplication.translate(
+ "DBManagerPlugin", "Refresh the materialized view"
+ )
+ ),
+ "",
+ )
+ )
ret.append(HtmlTable(tbl))
return ret
@@ -529,8 +628,7 @@ def getViewInfo(self):
# Syntax highlight
lexer = get_lexer_by_name("sql")
- formatter = HtmlFormatter(
- linenos=True, cssclass="source", noclasses=True)
+ formatter = HtmlFormatter(linenos=True, cssclass="source", noclasses=True)
result = highlight(view_def, lexer, formatter)
if view_def:
@@ -539,9 +637,8 @@ def getViewInfo(self):
else:
title = "Materialized View Definition"
ret.append(
- HtmlSection(
- QApplication.translate("DBManagerPlugin", title),
- result))
+ HtmlSection(QApplication.translate("DBManagerPlugin", title), result)
+ )
return ret
@@ -565,35 +662,41 @@ def spatialInfo(self):
return ret
tbl = [
- (QApplication.translate("DBManagerPlugin", "Column:"),
- self.table.geomColumn),
- (QApplication.translate("DBManagerPlugin", "Geometry:"),
- self.table.geomType),
- (QApplication.translate("DBManagerPlugin",
- "QGIS Geometry type:"),
- QgsWkbTypes.displayString(self.table.wkbType))
+ (
+ QApplication.translate("DBManagerPlugin", "Column:"),
+ self.table.geomColumn,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "Geometry:"),
+ self.table.geomType,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "QGIS Geometry type:"),
+ QgsWkbTypes.displayString(self.table.wkbType),
+ ),
]
# only if we have info from geometry_columns
if self.table.geomDim:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin",
- "Dimension:"),
- self.table.geomDim))
+ (
+ QApplication.translate("DBManagerPlugin", "Dimension:"),
+ self.table.geomDim,
+ )
+ )
srid = self.table.srid if self.table.srid else -1
if srid != -1:
- sr_info = (
- self.table.database().connector.getSpatialRefInfo(srid))
+ sr_info = self.table.database().connector.getSpatialRefInfo(srid)
else:
- sr_info = QApplication.translate("DBManagerPlugin",
- "Undefined")
+ sr_info = QApplication.translate("DBManagerPlugin", "Undefined")
if sr_info:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Spatial ref:"),
- "{} ({})".format(sr_info, srid)))
+ (
+ QApplication.translate("DBManagerPlugin", "Spatial ref:"),
+ f"{sr_info} ({srid})",
+ )
+ )
# estimated extent
if not self.table.estimatedExtent:
@@ -605,53 +708,65 @@ def spatialInfo(self):
self.table.blockSignals(False)
if self.table.estimatedExtent:
- estimated_extent_str = ("{:.9f}, {:.9f} - {:.9f}, "
- "{:.9f}".format(
- *self.table.estimatedExtent))
+ estimated_extent_str = "{:.9f}, {:.9f} - {:.9f}, " "{:.9f}".format(
+ *self.table.estimatedExtent
+ )
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Estimated extent:"),
- estimated_extent_str))
+ (
+ QApplication.translate("DBManagerPlugin", "Estimated extent:"),
+ estimated_extent_str,
+ )
+ )
# extent
extent_str = None
if self.table.extent and len(self.table.extent) == 4:
- extent_str = ("{:.9f}, {:.9f} - {:.9f}, "
- "{:.9f}".format(*self.table.extent))
- elif (self.table.rowCount is not None and self.table.rowCount > 0) or (self.table.estimatedRowCount is not None and self.table.estimatedRowCount > 0):
+ extent_str = "{:.9f}, {:.9f} - {:.9f}, " "{:.9f}".format(*self.table.extent)
+ elif (self.table.rowCount is not None and self.table.rowCount > 0) or (
+ self.table.estimatedRowCount is not None
+ and self.table.estimatedRowCount > 0
+ ):
# Can't calculate an extent on empty layer
extent_str = QApplication.translate(
"DBManagerPlugin",
- '(unknown) (find out)')
+ '(unknown) (find out)',
+ )
if extent_str:
tbl.append(
- (QApplication.translate(
- "DBManagerPlugin", "Extent:"),
- extent_str))
+ (QApplication.translate("DBManagerPlugin", "Extent:"), extent_str)
+ )
ret.append(HtmlTable(tbl))
# Handle extent update metadata
- if (self.table.extent and
- self.table.extent != self.table.estimatedExtent and
- self.table.canUpdateMetadata()):
+ if (
+ self.table.extent
+ and self.table.extent != self.table.estimatedExtent
+ and self.table.canUpdateMetadata()
+ ):
ret.append(
HtmlParagraph(
QApplication.translate(
"DBManagerPlugin",
- ' Metadata extent is different from'
+ " Metadata extent is different from"
' real extent. You should update it!')))
+ '/update">update it!',
+ )
+ )
+ )
# is there an entry in geometry_columns?
- if self.table.geomType.lower() == 'geometry':
+ if self.table.geomType.lower() == "geometry":
ret.append(
HtmlParagraph(
QApplication.translate(
"DBManagerPlugin",
- " There is no entry in geometry_columns!")))
+ " There is no entry in geometry_columns!",
+ )
+ )
+ )
# find out whether the geometry column has spatial index on it
if not self.table.isView:
@@ -660,8 +775,11 @@ def spatialInfo(self):
HtmlParagraph(
QApplication.translate(
"DBManagerPlugin",
- ' No spatial index defined ('
- 'create it).')))
+ "create it).",
+ )
+ )
+ )
return ret
diff --git a/python/plugins/db_manager/db_plugins/oracle/plugin.py b/python/plugins/db_manager/db_plugins/oracle/plugin.py
index 2c19063065ca..d3de0e4c70fe 100644
--- a/python/plugins/db_manager/db_plugins/oracle/plugin.py
+++ b/python/plugins/db_manager/db_plugins/oracle/plugin.py
@@ -22,10 +22,7 @@
"""
# this will disable the dbplugin if the connector raise an ImportError
-from typing import (
- Optional,
- Union
-)
+from typing import Optional, Union
from .connector import OracleDBConnector
@@ -35,9 +32,19 @@
from qgis.core import QgsApplication, QgsVectorLayer, NULL, QgsSettings
-from ..plugin import ConnectionError, InvalidDataException, DBPlugin, \
- Database, Schema, Table, VectorTable, TableField, TableConstraint, \
- TableIndex, TableTrigger
+from ..plugin import (
+ ConnectionError,
+ InvalidDataException,
+ DBPlugin,
+ Database,
+ Schema,
+ Table,
+ VectorTable,
+ TableField,
+ TableConstraint,
+ TableIndex,
+ TableTrigger,
+)
from qgis.core import QgsCredentials
@@ -54,19 +61,19 @@ def icon(self):
@classmethod
def typeName(self):
- return 'oracle'
+ return "oracle"
@classmethod
def typeNameString(self):
- return QCoreApplication.translate('db_manager', 'Oracle Spatial')
+ return QCoreApplication.translate("db_manager", "Oracle Spatial")
@classmethod
def providerName(self):
- return 'oracle'
+ return "oracle"
@classmethod
def connectionSettingsKey(self):
- return '/Oracle/connections'
+ return "/Oracle/connections"
def connectToUri(self, uri):
self.db = self.databasesFactory(self, uri)
@@ -80,35 +87,44 @@ def databasesFactory(self, connection, uri):
def connect(self, parent=None):
conn_name = self.connectionName()
settings = QgsSettings()
- settings.beginGroup("/{}/{}".format(
- self.connectionSettingsKey(), conn_name))
+ settings.beginGroup(f"/{self.connectionSettingsKey()}/{conn_name}")
if not settings.contains("database"): # non-existent entry?
raise InvalidDataException(
- self.tr('There is no defined database connection "{}".'.format(
- conn_name)))
+ self.tr(f'There is no defined database connection "{conn_name}".')
+ )
from qgis.core import QgsDataSourceUri
+
uri = QgsDataSourceUri()
settingsList = ["host", "port", "database", "username", "password"]
host, port, database, username, password = (
- settings.value(x, "", type=str) for x in settingsList)
+ settings.value(x, "", type=str) for x in settingsList
+ )
# get all of the connection options
- useEstimatedMetadata = settings.value(
- "estimatedMetadata", False, type=bool)
- uri.setParam('userTablesOnly', str(
- settings.value("userTablesOnly", False, type=bool)))
- uri.setParam('geometryColumnsOnly', str(
- settings.value("geometryColumnsOnly", False, type=bool)))
- uri.setParam('allowGeometrylessTables', str(
- settings.value("allowGeometrylessTables", False, type=bool)))
- uri.setParam('onlyExistingTypes', str(
- settings.value("onlyExistingTypes", False, type=bool)))
- uri.setParam('includeGeoAttributes', str(
- settings.value("includeGeoAttributes", False, type=bool)))
+ useEstimatedMetadata = settings.value("estimatedMetadata", False, type=bool)
+ uri.setParam(
+ "userTablesOnly", str(settings.value("userTablesOnly", False, type=bool))
+ )
+ uri.setParam(
+ "geometryColumnsOnly",
+ str(settings.value("geometryColumnsOnly", False, type=bool)),
+ )
+ uri.setParam(
+ "allowGeometrylessTables",
+ str(settings.value("allowGeometrylessTables", False, type=bool)),
+ )
+ uri.setParam(
+ "onlyExistingTypes",
+ str(settings.value("onlyExistingTypes", False, type=bool)),
+ )
+ uri.setParam(
+ "includeGeoAttributes",
+ str(settings.value("includeGeoAttributes", False, type=bool)),
+ )
settings.endGroup()
@@ -126,7 +142,8 @@ def connect(self, parent=None):
max_attempts = 3
for i in range(max_attempts):
(ok, username, password) = QgsCredentials.instance().get(
- uri.connectionInfo(False), username, password, err)
+ uri.connectionInfo(False), username, password, err
+ )
if not ok:
return False
@@ -141,8 +158,7 @@ def connect(self, parent=None):
err = str(e)
continue
- QgsCredentials.instance().put(
- uri.connectionInfo(False), username, password)
+ QgsCredentials.instance().put(uri.connectionInfo(False), username, password)
return True
@@ -166,6 +182,7 @@ def vectorTablesFactory(self, row, db, schema=None):
def info(self):
from .info_model import ORDatabaseInfo
+
return ORDatabaseInfo(self)
def schemasFactory(self, row, db):
@@ -174,7 +191,7 @@ def schemasFactory(self, row, db):
def columnUniqueValuesModel(self, col, table, limit=10):
l = ""
if limit:
- l = "WHERE ROWNUM < {:d}".format(limit)
+ l = f"WHERE ROWNUM < {limit:d}"
con = self.database().connector
# Prevent geometry column show
tableName = table.replace('"', "").split(".")
@@ -185,11 +202,12 @@ def columnUniqueValuesModel(self, col, table, limit=10):
if con.isGeometryColumn(tableName, colName):
return None
- query = "SELECT DISTINCT {} FROM {} {}".format(col, table, l)
+ query = f"SELECT DISTINCT {col} FROM {table} {l}"
return self.sqlResultModel(query, self)
def sqlResultModel(self, sql, parent):
from .data_model import ORSqlResultModel
+
return ORSqlResultModel(self, sql, parent)
def sqlResultModelAsync(self, sql, parent):
@@ -197,9 +215,16 @@ def sqlResultModelAsync(self, sql, parent):
return ORSqlResultModelAsync(self, sql, parent)
- def toSqlLayer(self, sql, geomCol, uniqueCol,
- layerName="QueryLayer", layerType=None,
- avoidSelectById=False, filter=""):
+ def toSqlLayer(
+ self,
+ sql,
+ geomCol,
+ uniqueCol,
+ layerName="QueryLayer",
+ layerType=None,
+ avoidSelectById=False,
+ filter="",
+ ):
uri = self.uri()
con = self.database().connector
@@ -207,8 +232,7 @@ def toSqlLayer(self, sql, geomCol, uniqueCol,
if uniqueCol is not None:
uniqueCol = uniqueCol.strip('"').replace('""', '"')
- uri.setDataSource("", "({}\n)".format(
- sql), geomCol, filter, uniqueCol)
+ uri.setDataSource("", f"({sql}\n)", geomCol, filter, uniqueCol)
if avoidSelectById:
uri.disableSelectAtId(True)
@@ -218,8 +242,7 @@ def toSqlLayer(self, sql, geomCol, uniqueCol,
# handling undetermined geometry type
if not vlayer.isValid():
- wkbType, srid = con.getTableMainGeomType(
- "({}\n)".format(sql), geomCol)
+ wkbType, srid = con.getTableMainGeomType(f"({sql}\n)", geomCol)
uri.setWkbType(wkbType)
if srid:
uri.setSrid(str(srid))
@@ -228,45 +251,76 @@ def toSqlLayer(self, sql, geomCol, uniqueCol,
return vlayer
def registerDatabaseActions(self, mainWindow):
- action = QAction(QApplication.translate(
- "DBManagerPlugin", "&Re-connect"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Database"), self.reconnectActionSlot)
+ action = QAction(QApplication.translate("DBManagerPlugin", "&Re-connect"), self)
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Database"),
+ self.reconnectActionSlot,
+ )
if self.schemas():
- action = QAction(QApplication.translate(
- "DBManagerPlugin", "&Create Schema…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Schema"), self.createSchemaActionSlot)
- action = QAction(QApplication.translate(
- "DBManagerPlugin", "&Delete (Empty) Schema…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Schema"), self.deleteSchemaActionSlot)
-
- action = QAction(QApplication.translate(
- "DBManagerPlugin", "Delete Selected Item"), self)
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Create Schema…"), self
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Schema"),
+ self.createSchemaActionSlot,
+ )
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Delete (Empty) Schema…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Schema"),
+ self.deleteSchemaActionSlot,
+ )
+
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "Delete Selected Item"), self
+ )
mainWindow.registerAction(action, None, self.deleteActionSlot)
action.setShortcuts(QKeySequence.StandardKey.Delete)
- action = QAction(QgsApplication.getThemeIcon("/mActionCreateTable.svg"),
- QApplication.translate(
- "DBManagerPlugin", "&Create Table…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Table"), self.createTableActionSlot)
- action = QAction(QgsApplication.getThemeIcon("/mActionEditTable.svg"),
- QApplication.translate(
- "DBManagerPlugin", "&Edit Table…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Table"), self.editTableActionSlot)
- action = QAction(QgsApplication.getThemeIcon("/mActionDeleteTable.svg"),
- QApplication.translate(
- "DBManagerPlugin", "&Delete Table/View…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Table"), self.deleteTableActionSlot)
- action = QAction(QApplication.translate(
- "DBManagerPlugin", "&Empty Table…"), self)
- mainWindow.registerAction(action, QApplication.translate(
- "DBManagerPlugin", "&Table"), self.emptyTableActionSlot)
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionCreateTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Create Table…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.createTableActionSlot,
+ )
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionEditTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Edit Table…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.editTableActionSlot,
+ )
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionDeleteTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Delete Table/View…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.deleteTableActionSlot,
+ )
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Empty Table…"), self
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.emptyTableActionSlot,
+ )
def supportsComment(self):
return False
@@ -298,36 +352,40 @@ def __init__(self, row, db, schema=None):
def getDates(self):
"""Grab the creation/modification dates of the table"""
self.creationDate, self.modificationDate = (
- self.database().connector.getTableDates((self.schemaName(),
- self.name)))
+ self.database().connector.getTableDates((self.schemaName(), self.name))
+ )
def refreshRowEstimation(self):
"""Use ALL_ALL_TABLE to get an estimation of rows"""
if self.isView:
self.estimatedRowCount = 0
- self.estimatedRowCount = (
- self.database().connector.getTableRowEstimation(
- (self.schemaName(), self.name)))
+ self.estimatedRowCount = self.database().connector.getTableRowEstimation(
+ (self.schemaName(), self.name)
+ )
def getType(self):
"""Grab the type of object for the table"""
self.objectType = self.database().connector.getTableType(
- (self.schemaName(), self.name))
+ (self.schemaName(), self.name)
+ )
def getComment(self):
"""Grab the general comment of the table/view"""
self.comment = self.database().connector.getTableComment(
- (self.schemaName(), self.name), self.objectType)
+ (self.schemaName(), self.name), self.objectType
+ )
def getDefinition(self):
return self.database().connector.getDefinition(
- (self.schemaName(), self.name), self.objectType)
+ (self.schemaName(), self.name), self.objectType
+ )
def getMViewInfo(self):
if self.objectType == "MATERIALIZED VIEW":
return self.database().connector.getMViewInfo(
- (self.schemaName(), self.name))
+ (self.schemaName(), self.name)
+ )
else:
return None
@@ -339,22 +397,25 @@ def runAction(self, action):
self.refreshRowCount()
return True
elif action.startswith("index/"):
- parts = action.split('/')
+ parts = action.split("/")
index_name = parts[1]
index_action = parts[2]
msg = QApplication.translate(
"DBManagerPlugin",
- "Do you want to {} index {}?".format(
- index_action, index_name))
+ f"Do you want to {index_action} index {index_name}?",
+ )
QApplication.restoreOverrideCursor()
try:
- if QMessageBox.question(
+ if (
+ QMessageBox.question(
None,
- QApplication.translate(
- "DBManagerPlugin", "Table Index"),
+ QApplication.translate("DBManagerPlugin", "Table Index"),
msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return False
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -362,14 +423,14 @@ def runAction(self, action):
if index_action == "rebuild":
self.aboutToChange.emit()
self.database().connector.rebuildTableIndex(
- (self.schemaName(), self.name), index_name)
+ (self.schemaName(), self.name), index_name
+ )
self.refreshIndexes()
return True
elif action.startswith("mview/"):
if action == "mview/refresh":
self.aboutToChange.emit()
- self.database().connector.refreshMView(
- (self.schemaName(), self.name))
+ self.database().connector.refreshMView((self.schemaName(), self.name))
return True
return Table.runAction(self, action)
@@ -388,14 +449,16 @@ def tableTriggersFactory(self, row, table):
def info(self):
from .info_model import ORTableInfo
+
return ORTableInfo(self)
def tableDataModel(self, parent):
from .data_model import ORTableDataModel
+
return ORTableDataModel(self, parent)
def getValidQgisUniqueFields(self, onlyOne=False):
- """ list of fields valid to load the table as layer in QGIS canvas.
+ """list of fields valid to load the table as layer in QGIS canvas.
QGIS automatically search for a valid unique field, so it's
needed only for queries and views.
"""
@@ -413,12 +476,22 @@ def getValidQgisUniqueFields(self, onlyOne=False):
for idx in indexes:
if idx.isUnique and len(idx.columns) == 1:
fld = idx.fields()[idx.columns[0]]
- if (fld.dataType == "NUMBER" and not fld.modifier and fld.notNull and fld not in ret):
+ if (
+ fld.dataType == "NUMBER"
+ and not fld.modifier
+ and fld.notNull
+ and fld not in ret
+ ):
ret.append(fld)
# and finally append the other suitable fields
for fld in self.fields():
- if (fld.dataType == "NUMBER" and not fld.modifier and fld.notNull and fld not in ret):
+ if (
+ fld.dataType == "NUMBER"
+ and not fld.modifier
+ and fld.notNull
+ and fld not in ret
+ ):
ret.append(fld)
if onlyOne:
@@ -427,13 +500,18 @@ def getValidQgisUniqueFields(self, onlyOne=False):
def uri(self):
uri = self.database().uri()
- schema = self.schemaName() if self.schemaName() else ''
- geomCol = self.geomColumn if self.type in [
- Table.VectorType, Table.RasterType] else ""
- uniqueCol = self.getValidQgisUniqueFields(
- True) if self.isView else None
- uri.setDataSource(schema, self.name, geomCol if geomCol else None,
- None, uniqueCol.name if uniqueCol else "")
+ schema = self.schemaName() if self.schemaName() else ""
+ geomCol = (
+ self.geomColumn if self.type in [Table.VectorType, Table.RasterType] else ""
+ )
+ uniqueCol = self.getValidQgisUniqueFields(True) if self.isView else None
+ uri.setDataSource(
+ schema,
+ self.name,
+ geomCol if geomCol else None,
+ None,
+ uniqueCol.name if uniqueCol else "",
+ )
# Handle geographic table
if geomCol:
@@ -448,11 +526,13 @@ class ORVectorTable(ORTable, VectorTable):
def __init__(self, row, db, schema=None):
ORTable.__init__(self, row[0:3], db, schema)
VectorTable.__init__(self, db, schema)
- self.geomColumn, self.geomType, self.wkbType, self.geomDim, \
- self.srid = row[-7:-2]
+ self.geomColumn, self.geomType, self.wkbType, self.geomDim, self.srid = row[
+ -7:-2
+ ]
def info(self):
from .info_model import ORVectorTableInfo
+
return ORVectorTableInfo(self)
def runAction(self, action):
@@ -468,13 +548,14 @@ def runAction(self, action):
return VectorTable.runAction(self, action)
def canUpdateMetadata(self):
- return self.database().connector.canUpdateMetadata((self.schemaName(),
- self.name))
+ return self.database().connector.canUpdateMetadata(
+ (self.schemaName(), self.name)
+ )
def updateExtent(self):
self.database().connector.updateMetadata(
- (self.schemaName(), self.name),
- self.geomColumn, extent=self.extent)
+ (self.schemaName(), self.name), self.geomColumn, extent=self.extent
+ )
self.refreshTableEstimatedExtent()
self.refresh()
@@ -490,11 +571,20 @@ def hasSpatialIndex(self, geom_column=None):
class ORTableField(TableField):
def __init__(self, row, table):
- """ build fields information from query and find primary key """
+ """build fields information from query and find primary key"""
TableField.__init__(self, table)
- self.num, self.name, self.dataType, self.charMaxLen, \
- self.modifier, self.notNull, self.hasDefault, \
- self.default, typeStr, self.comment = row
+ (
+ self.num,
+ self.name,
+ self.dataType,
+ self.charMaxLen,
+ self.modifier,
+ self.notNull,
+ self.hasDefault,
+ self.default,
+ typeStr,
+ self.comment,
+ ) = row
self.primaryKey = False
self.num = int(self.num)
@@ -523,18 +613,23 @@ def __init__(self, row, table):
break
def type2String(self):
- if ("TIMESTAMP" in self.dataType or self.dataType in ["DATE", "SDO_GEOMETRY", "BINARY_FLOAT", "BINARY_DOUBLE"]):
- return "{}".format(self.dataType)
+ if "TIMESTAMP" in self.dataType or self.dataType in [
+ "DATE",
+ "SDO_GEOMETRY",
+ "BINARY_FLOAT",
+ "BINARY_DOUBLE",
+ ]:
+ return f"{self.dataType}"
if self.charMaxLen in [None, -1]:
- return "{}".format(self.dataType)
+ return f"{self.dataType}"
elif self.modifier in [None, -1, 0]:
- return "{}({})".format(self.dataType, self.charMaxLen)
+ return f"{self.dataType}({self.charMaxLen})"
- return "{}({},{})".format(self.dataType, self.charMaxLen,
- self.modifier)
+ return f"{self.dataType}({self.charMaxLen},{self.modifier})"
- def update(self, new_name, new_type_str=None, new_not_null=None,
- new_default_str=None):
+ def update(
+ self, new_name, new_type_str=None, new_not_null=None, new_default_str=None
+ ):
self.table().aboutToChange.emit()
if self.name == new_name:
new_name = None
@@ -545,10 +640,18 @@ def update(self, new_name, new_type_str=None, new_not_null=None,
if self.default2String() == new_default_str:
new_default_str = None
- ret = self.table().database().connector.updateTableColumn(
- (self.table().schemaName(), self.table().name),
- self.name, new_name, new_type_str,
- new_not_null, new_default_str)
+ ret = (
+ self.table()
+ .database()
+ .connector.updateTableColumn(
+ (self.table().schemaName(), self.table().name),
+ self.name,
+ new_name,
+ new_type_str,
+ new_not_null,
+ new_default_str,
+ )
+ )
# When changing a field, refresh also constraints and
# indexes.
@@ -560,17 +663,21 @@ def update(self, new_name, new_type_str=None, new_not_null=None,
class ORTableConstraint(TableConstraint):
- TypeCheck, TypeForeignKey, TypePrimaryKey, \
- TypeUnique, TypeUnknown = list(range(5))
+ TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique, TypeUnknown = list(range(5))
- types = {"c": TypeCheck, "r": TypeForeignKey,
- "p": TypePrimaryKey, "u": TypeUnique}
+ types = {"c": TypeCheck, "r": TypeForeignKey, "p": TypePrimaryKey, "u": TypeUnique}
def __init__(self, row, table):
- """ build constraints info from query """
+ """build constraints info from query"""
TableConstraint.__init__(self, table)
- self.name, constr_type_str, self.column, self.validated, \
- self.generated, self.status = row[0:6]
+ (
+ self.name,
+ constr_type_str,
+ self.column,
+ self.validated,
+ self.generated,
+ self.status,
+ ) = row[0:6]
constr_type_str = constr_type_str.lower()
if constr_type_str in ORTableConstraint.types:
@@ -608,10 +715,10 @@ def type2String(self):
if self.type == ORTableConstraint.TypeUnique:
return QApplication.translate("DBManagerPlugin", "Unique")
- return QApplication.translate("DBManagerPlugin", 'Unknown')
+ return QApplication.translate("DBManagerPlugin", "Unknown")
def fields(self):
- """ Hack to make edit dialog box work """
+ """Hack to make edit dialog box work"""
fields = self.table().fields()
field = None
for fld in fields:
@@ -627,11 +734,18 @@ class ORTableIndex(TableIndex):
def __init__(self, row, table):
TableIndex.__init__(self, table)
- self.name, self.column, self.indexType, self.status, \
- self.analyzed, self.compression, self.isUnique = row
+ (
+ self.name,
+ self.column,
+ self.indexType,
+ self.status,
+ self.analyzed,
+ self.compression,
+ self.isUnique,
+ ) = row
def fields(self):
- """ Hack to make edit dialog box work """
+ """Hack to make edit dialog box work"""
self.table().refreshFields()
fields = self.table().fields()
diff --git a/python/plugins/db_manager/db_plugins/oracle/sql_dictionary.py b/python/plugins/db_manager/db_plugins/oracle/sql_dictionary.py
index 46e7e4211d37..4632abe69378 100644
--- a/python/plugins/db_manager/db_plugins/oracle/sql_dictionary.py
+++ b/python/plugins/db_manager/db_plugins/oracle/sql_dictionary.py
@@ -21,91 +21,481 @@
***************************************************************************/
"""
-__author__ = 'Médéric RIBREUX'
-__date__ = 'August 2014'
-__copyright__ = '(C) 2014, Médéric RIBREUX'
+__author__ = "Médéric RIBREUX"
+__date__ = "August 2014"
+__copyright__ = "(C) 2014, Médéric RIBREUX"
# keywords
keywords = [
# From:
# http://docs.oracle.com/cd/B19306_01/server.102/b14200/ap_keywd.htm
- "ACCESS", "ADD", "ALL", "ALTER", "AND", "ANY", "AS", "ASC",
- "AUDIT", "BETWEEN", "BY", "CHAR", "CHECK", "CLUSTER", "COLUMN",
- "COMMENT", "COMPRESS", "CONNECT", "CREATE", "CURRENT", "DATE",
- "DECIMAL", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP",
- "ELSE", "EXCLUSIVE", "EXISTS", "FILE", "FLOAT", "FOR", "FROM",
- "GRANT", "GROUP", "HAVING", "IDENTIFIED", "IMMEDIATE", "IN",
- "INCREMENT", "INDEX", "INITIAL", "INSERT", "INTEGER", "INTERSECT",
- "INTO", "IS", "LEVEL", "LIKE", "LOCK", "LONG", "MAXEXTENTS",
- "MINUS", "MLSLABEL", "MODE", "MODIFY", "NOAUDIT", "NOCOMPRESS",
- "NOT", "NOWAIT", "NULL", "NUMBER", "OF", "OFFLINE", "ON",
- "ONLINE", "OPTION", "OR", "ORDER", "PCTFREE", "PRIOR",
- "PRIVILEGES", "PUBLIC", "RAW", "RENAME", "RESOURCE", "REVOKE",
- "ROW", "ROWID", "ROWNUM", "ROWS", "SELECT", "SESSION", "SET",
- "SHARE", "SIZE", "SMALLINT", "START", "SUCCESSFUL", "SYNONYM",
- "SYSDATE", "TABLE", "THEN", "TO", "TRIGGER", "UID", "UNION",
- "UNIQUE", "UPDATE", "USER", "VALIDATE", "VALUES", "VARCHAR",
- "VARCHAR2", "VIEW", "WHENEVER", "WHERE", "WITH",
+ "ACCESS",
+ "ADD",
+ "ALL",
+ "ALTER",
+ "AND",
+ "ANY",
+ "AS",
+ "ASC",
+ "AUDIT",
+ "BETWEEN",
+ "BY",
+ "CHAR",
+ "CHECK",
+ "CLUSTER",
+ "COLUMN",
+ "COMMENT",
+ "COMPRESS",
+ "CONNECT",
+ "CREATE",
+ "CURRENT",
+ "DATE",
+ "DECIMAL",
+ "DEFAULT",
+ "DELETE",
+ "DESC",
+ "DISTINCT",
+ "DROP",
+ "ELSE",
+ "EXCLUSIVE",
+ "EXISTS",
+ "FILE",
+ "FLOAT",
+ "FOR",
+ "FROM",
+ "GRANT",
+ "GROUP",
+ "HAVING",
+ "IDENTIFIED",
+ "IMMEDIATE",
+ "IN",
+ "INCREMENT",
+ "INDEX",
+ "INITIAL",
+ "INSERT",
+ "INTEGER",
+ "INTERSECT",
+ "INTO",
+ "IS",
+ "LEVEL",
+ "LIKE",
+ "LOCK",
+ "LONG",
+ "MAXEXTENTS",
+ "MINUS",
+ "MLSLABEL",
+ "MODE",
+ "MODIFY",
+ "NOAUDIT",
+ "NOCOMPRESS",
+ "NOT",
+ "NOWAIT",
+ "NULL",
+ "NUMBER",
+ "OF",
+ "OFFLINE",
+ "ON",
+ "ONLINE",
+ "OPTION",
+ "OR",
+ "ORDER",
+ "PCTFREE",
+ "PRIOR",
+ "PRIVILEGES",
+ "PUBLIC",
+ "RAW",
+ "RENAME",
+ "RESOURCE",
+ "REVOKE",
+ "ROW",
+ "ROWID",
+ "ROWNUM",
+ "ROWS",
+ "SELECT",
+ "SESSION",
+ "SET",
+ "SHARE",
+ "SIZE",
+ "SMALLINT",
+ "START",
+ "SUCCESSFUL",
+ "SYNONYM",
+ "SYSDATE",
+ "TABLE",
+ "THEN",
+ "TO",
+ "TRIGGER",
+ "UID",
+ "UNION",
+ "UNIQUE",
+ "UPDATE",
+ "USER",
+ "VALIDATE",
+ "VALUES",
+ "VARCHAR",
+ "VARCHAR2",
+ "VIEW",
+ "WHENEVER",
+ "WHERE",
+ "WITH",
# From http://docs.oracle.com/cd/B13789_01/appdev.101/a42525/apb.htm
- "ADMIN", "CURSOR", "FOUND", "MOUNT", "AFTER", "CYCLE", "FUNCTION",
- "NEXT", "ALLOCATE", "DATABASE", "GO", "NEW", "ANALYZE",
- "DATAFILE", "GOTO", "NOARCHIVELOG", "ARCHIVE", "DBA", "GROUPS",
- "NOCACHE", "ARCHIVELOG", "DEC", "INCLUDING", "NOCYCLE",
- "AUTHORIZATION", "DECLARE", "INDICATOR", "NOMAXVALUE", "AVG",
- "DISABLE", "INITRANS", "NOMINVALUE", "BACKUP", "DISMOUNT",
- "INSTANCE", "NONE", "BEGIN", "DOUBLE", "INT", "NOORDER", "BECOME",
- "DUMP", "KEY", "NORESETLOGS", "BEFORE", "EACH", "LANGUAGE",
- "NORMAL", "BLOCK", "ENABLE", "LAYER", "NOSORT", "BODY", "END",
- "LINK", "NUMERIC", "CACHE", "ESCAPE", "LISTS", "OFF", "CANCEL",
- "EVENTS", "LOGFILE", "OLD", "CASCADE", "EXCEPT", "MANAGE", "ONLY",
- "CHANGE", "EXCEPTIONS", "MANUAL", "OPEN", "CHARACTER", "EXEC",
- "MAX", "OPTIMAL", "CHECKPOINT", "EXPLAIN", "MAXDATAFILES", "OWN",
- "CLOSE", "EXECUTE", "MAXINSTANCES", "PACKAGE", "COBOL", "EXTENT",
- "MAXLOGFILES", "PARALLEL", "COMMIT", "EXTERNALLY",
- "MAXLOGHISTORY", "PCTINCREASE", "COMPILE", "FETCH",
- "MAXLOGMEMBERS", "PCTUSED", "CONSTRAINT", "FLUSH", "MAXTRANS",
- "PLAN", "CONSTRAINTS", "FREELIST", "MAXVALUE", "PLI", "CONTENTS",
- "FREELISTS", "MIN", "PRECISION", "CONTINUE", "FORCE",
- "MINEXTENTS", "PRIMARY", "CONTROLFILE", "FOREIGN", "MINVALUE",
- "PRIVATE", "COUNT", "FORTRAN", "MODULE", "PROCEDURE", "PROFILE",
- "SAVEPOINT", "SQLSTATE", "TRACING", "QUOTA", "SCHEMA",
- "STATEMENT_ID", "TRANSACTION", "READ", "SCN", "STATISTICS",
- "TRIGGERS", "REAL", "SECTION", "STOP", "TRUNCATE", "RECOVER",
- "SEGMENT", "STORAGE", "UNDER", "REFERENCES", "SEQUENCE", "SUM",
- "UNLIMITED", "REFERENCING", "SHARED", "SWITCH", "UNTIL",
- "RESETLOGS", "SNAPSHOT", "SYSTEM", "USE", "RESTRICTED", "SOME",
- "TABLES", "USING", "REUSE", "SORT", "TABLESPACE", "WHEN", "ROLE",
- "SQL", "TEMPORARY", "WRITE", "ROLES", "SQLCODE", "THREAD", "WORK",
- "ROLLBACK", "SQLERROR", "TIME", "ABORT", "BETWEEN", "CRASH",
- "DIGITS", "ACCEPT", "BINARY_INTEGER", "CREATE", "DISPOSE",
- "ACCESS", "BODY", "CURRENT", "DISTINCT", "ADD", "BOOLEAN",
- "CURRVAL", "DO", "ALL", "BY", "CURSOR", "DROP", "ALTER", "CASE",
- "DATABASE", "ELSE", "AND", "CHAR", "DATA_BASE", "ELSIF", "ANY",
- "CHAR_BASE", "DATE", "END", "ARRAY", "CHECK", "DBA", "ENTRY",
- "ARRAYLEN", "CLOSE", "DEBUGOFF", "EXCEPTION", "AS", "CLUSTER",
- "DEBUGON", "EXCEPTION_INIT", "ASC", "CLUSTERS", "DECLARE",
- "EXISTS", "ASSERT", "COLAUTH", "DECIMAL", "EXIT", "ASSIGN",
- "COLUMNS", "DEFAULT", "FALSE", "AT", "COMMIT", "DEFINITION",
- "FETCH", "AUTHORIZATION", "COMPRESS", "DELAY", "FLOAT", "AVG",
- "CONNECT", "DELETE", "FOR", "BASE_TABLE", "CONSTANT", "DELTA",
- "FORM", "BEGIN", "COUNT", "DESC", "FROM", "FUNCTION", "NEW",
- "RELEASE", "SUM", "GENERIC", "NEXTVAL", "REMR", "TABAUTH", "GOTO",
- "NOCOMPRESS", "RENAME", "TABLE", "GRANT", "NOT", "RESOURCE",
- "TABLES", "GROUP", "NULL", "RETURN", "TASK", "HAVING", "NUMBER",
- "REVERSE", "TERMINATE", "IDENTIFIED", "NUMBER_BASE", "REVOKE",
- "THEN", "IF", "OF", "ROLLBACK", "TO", "IN", "ON", "ROWID", "TRUE",
- "INDEX", "OPEN", "ROWLABEL", "TYPE", "INDEXES", "OPTION",
- "ROWNUM", "UNION", "INDICATOR", "OR", "ROWTYPE", "UNIQUE",
- "INSERT", "ORDER", "RUN", "UPDATE", "INTEGER", "OTHERS",
- "SAVEPOINT", "USE", "INTERSECT", "OUT", "SCHEMA", "VALUES",
- "INTO", "PACKAGE", "SELECT", "VARCHAR", "IS", "PARTITION",
- "SEPARATE", "VARCHAR2", "LEVEL", "PCTFREE", "SET", "VARIANCE",
- "LIKE", "POSITIVE", "SIZE", "VIEW", "LIMITED", "PRAGMA",
- "SMALLINT", "VIEWS", "LOOP", "PRIOR", "SPACE", "WHEN", "MAX",
- "PRIVATE", "SQL", "WHERE", "MIN", "PROCEDURE", "SQLCODE", "WHILE",
- "MINUS", "PUBLIC", "SQLERRM", "WITH", "MLSLABEL", "RAISE",
- "START", "WORK", "MOD", "RANGE", "STATEMENT", "XOR", "MODE",
- "REAL", "STDDEV", "NATURAL", "RECORD", "SUBTYPE"
+ "ADMIN",
+ "CURSOR",
+ "FOUND",
+ "MOUNT",
+ "AFTER",
+ "CYCLE",
+ "FUNCTION",
+ "NEXT",
+ "ALLOCATE",
+ "DATABASE",
+ "GO",
+ "NEW",
+ "ANALYZE",
+ "DATAFILE",
+ "GOTO",
+ "NOARCHIVELOG",
+ "ARCHIVE",
+ "DBA",
+ "GROUPS",
+ "NOCACHE",
+ "ARCHIVELOG",
+ "DEC",
+ "INCLUDING",
+ "NOCYCLE",
+ "AUTHORIZATION",
+ "DECLARE",
+ "INDICATOR",
+ "NOMAXVALUE",
+ "AVG",
+ "DISABLE",
+ "INITRANS",
+ "NOMINVALUE",
+ "BACKUP",
+ "DISMOUNT",
+ "INSTANCE",
+ "NONE",
+ "BEGIN",
+ "DOUBLE",
+ "INT",
+ "NOORDER",
+ "BECOME",
+ "DUMP",
+ "KEY",
+ "NORESETLOGS",
+ "BEFORE",
+ "EACH",
+ "LANGUAGE",
+ "NORMAL",
+ "BLOCK",
+ "ENABLE",
+ "LAYER",
+ "NOSORT",
+ "BODY",
+ "END",
+ "LINK",
+ "NUMERIC",
+ "CACHE",
+ "ESCAPE",
+ "LISTS",
+ "OFF",
+ "CANCEL",
+ "EVENTS",
+ "LOGFILE",
+ "OLD",
+ "CASCADE",
+ "EXCEPT",
+ "MANAGE",
+ "ONLY",
+ "CHANGE",
+ "EXCEPTIONS",
+ "MANUAL",
+ "OPEN",
+ "CHARACTER",
+ "EXEC",
+ "MAX",
+ "OPTIMAL",
+ "CHECKPOINT",
+ "EXPLAIN",
+ "MAXDATAFILES",
+ "OWN",
+ "CLOSE",
+ "EXECUTE",
+ "MAXINSTANCES",
+ "PACKAGE",
+ "COBOL",
+ "EXTENT",
+ "MAXLOGFILES",
+ "PARALLEL",
+ "COMMIT",
+ "EXTERNALLY",
+ "MAXLOGHISTORY",
+ "PCTINCREASE",
+ "COMPILE",
+ "FETCH",
+ "MAXLOGMEMBERS",
+ "PCTUSED",
+ "CONSTRAINT",
+ "FLUSH",
+ "MAXTRANS",
+ "PLAN",
+ "CONSTRAINTS",
+ "FREELIST",
+ "MAXVALUE",
+ "PLI",
+ "CONTENTS",
+ "FREELISTS",
+ "MIN",
+ "PRECISION",
+ "CONTINUE",
+ "FORCE",
+ "MINEXTENTS",
+ "PRIMARY",
+ "CONTROLFILE",
+ "FOREIGN",
+ "MINVALUE",
+ "PRIVATE",
+ "COUNT",
+ "FORTRAN",
+ "MODULE",
+ "PROCEDURE",
+ "PROFILE",
+ "SAVEPOINT",
+ "SQLSTATE",
+ "TRACING",
+ "QUOTA",
+ "SCHEMA",
+ "STATEMENT_ID",
+ "TRANSACTION",
+ "READ",
+ "SCN",
+ "STATISTICS",
+ "TRIGGERS",
+ "REAL",
+ "SECTION",
+ "STOP",
+ "TRUNCATE",
+ "RECOVER",
+ "SEGMENT",
+ "STORAGE",
+ "UNDER",
+ "REFERENCES",
+ "SEQUENCE",
+ "SUM",
+ "UNLIMITED",
+ "REFERENCING",
+ "SHARED",
+ "SWITCH",
+ "UNTIL",
+ "RESETLOGS",
+ "SNAPSHOT",
+ "SYSTEM",
+ "USE",
+ "RESTRICTED",
+ "SOME",
+ "TABLES",
+ "USING",
+ "REUSE",
+ "SORT",
+ "TABLESPACE",
+ "WHEN",
+ "ROLE",
+ "SQL",
+ "TEMPORARY",
+ "WRITE",
+ "ROLES",
+ "SQLCODE",
+ "THREAD",
+ "WORK",
+ "ROLLBACK",
+ "SQLERROR",
+ "TIME",
+ "ABORT",
+ "BETWEEN",
+ "CRASH",
+ "DIGITS",
+ "ACCEPT",
+ "BINARY_INTEGER",
+ "CREATE",
+ "DISPOSE",
+ "ACCESS",
+ "BODY",
+ "CURRENT",
+ "DISTINCT",
+ "ADD",
+ "BOOLEAN",
+ "CURRVAL",
+ "DO",
+ "ALL",
+ "BY",
+ "CURSOR",
+ "DROP",
+ "ALTER",
+ "CASE",
+ "DATABASE",
+ "ELSE",
+ "AND",
+ "CHAR",
+ "DATA_BASE",
+ "ELSIF",
+ "ANY",
+ "CHAR_BASE",
+ "DATE",
+ "END",
+ "ARRAY",
+ "CHECK",
+ "DBA",
+ "ENTRY",
+ "ARRAYLEN",
+ "CLOSE",
+ "DEBUGOFF",
+ "EXCEPTION",
+ "AS",
+ "CLUSTER",
+ "DEBUGON",
+ "EXCEPTION_INIT",
+ "ASC",
+ "CLUSTERS",
+ "DECLARE",
+ "EXISTS",
+ "ASSERT",
+ "COLAUTH",
+ "DECIMAL",
+ "EXIT",
+ "ASSIGN",
+ "COLUMNS",
+ "DEFAULT",
+ "FALSE",
+ "AT",
+ "COMMIT",
+ "DEFINITION",
+ "FETCH",
+ "AUTHORIZATION",
+ "COMPRESS",
+ "DELAY",
+ "FLOAT",
+ "AVG",
+ "CONNECT",
+ "DELETE",
+ "FOR",
+ "BASE_TABLE",
+ "CONSTANT",
+ "DELTA",
+ "FORM",
+ "BEGIN",
+ "COUNT",
+ "DESC",
+ "FROM",
+ "FUNCTION",
+ "NEW",
+ "RELEASE",
+ "SUM",
+ "GENERIC",
+ "NEXTVAL",
+ "REMR",
+ "TABAUTH",
+ "GOTO",
+ "NOCOMPRESS",
+ "RENAME",
+ "TABLE",
+ "GRANT",
+ "NOT",
+ "RESOURCE",
+ "TABLES",
+ "GROUP",
+ "NULL",
+ "RETURN",
+ "TASK",
+ "HAVING",
+ "NUMBER",
+ "REVERSE",
+ "TERMINATE",
+ "IDENTIFIED",
+ "NUMBER_BASE",
+ "REVOKE",
+ "THEN",
+ "IF",
+ "OF",
+ "ROLLBACK",
+ "TO",
+ "IN",
+ "ON",
+ "ROWID",
+ "TRUE",
+ "INDEX",
+ "OPEN",
+ "ROWLABEL",
+ "TYPE",
+ "INDEXES",
+ "OPTION",
+ "ROWNUM",
+ "UNION",
+ "INDICATOR",
+ "OR",
+ "ROWTYPE",
+ "UNIQUE",
+ "INSERT",
+ "ORDER",
+ "RUN",
+ "UPDATE",
+ "INTEGER",
+ "OTHERS",
+ "SAVEPOINT",
+ "USE",
+ "INTERSECT",
+ "OUT",
+ "SCHEMA",
+ "VALUES",
+ "INTO",
+ "PACKAGE",
+ "SELECT",
+ "VARCHAR",
+ "IS",
+ "PARTITION",
+ "SEPARATE",
+ "VARCHAR2",
+ "LEVEL",
+ "PCTFREE",
+ "SET",
+ "VARIANCE",
+ "LIKE",
+ "POSITIVE",
+ "SIZE",
+ "VIEW",
+ "LIMITED",
+ "PRAGMA",
+ "SMALLINT",
+ "VIEWS",
+ "LOOP",
+ "PRIOR",
+ "SPACE",
+ "WHEN",
+ "MAX",
+ "PRIVATE",
+ "SQL",
+ "WHERE",
+ "MIN",
+ "PROCEDURE",
+ "SQLCODE",
+ "WHILE",
+ "MINUS",
+ "PUBLIC",
+ "SQLERRM",
+ "WITH",
+ "MLSLABEL",
+ "RAISE",
+ "START",
+ "WORK",
+ "MOD",
+ "RANGE",
+ "STATEMENT",
+ "XOR",
+ "MODE",
+ "REAL",
+ "STDDEV",
+ "NATURAL",
+ "RECORD",
+ "SUBTYPE",
]
oracle_spatial_keywords = []
@@ -115,154 +505,332 @@
functions = [
# FROM
# https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions001.htm
- "CAST", "COALESCE", "DECODE", "GREATEST", "LEAST", "LNNVL",
- "NULLIF", "NVL", "NVL2", "SET", "UID", "USER", "USERENV"
+ "CAST",
+ "COALESCE",
+ "DECODE",
+ "GREATEST",
+ "LEAST",
+ "LNNVL",
+ "NULLIF",
+ "NVL",
+ "NVL2",
+ "SET",
+ "UID",
+ "USER",
+ "USERENV",
]
# SQL math functions
math_functions = [
- 'ABS', 'ACOS', 'ASIN', 'ATAN', 'ATAN2', 'BITAND', 'CEIL', 'COS',
- 'COSH', 'EXP', 'FLOOR', 'LN', 'LOG', 'MOD', 'NANVL', 'POWER',
- 'REMAINDER', 'ROUND', 'SIGN', 'SIN', 'SINH', 'SQRT', 'TAN',
- 'TANH', 'TRUNC', 'WIDTH_BUCKET'
+ "ABS",
+ "ACOS",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "BITAND",
+ "CEIL",
+ "COS",
+ "COSH",
+ "EXP",
+ "FLOOR",
+ "LN",
+ "LOG",
+ "MOD",
+ "NANVL",
+ "POWER",
+ "REMAINDER",
+ "ROUND",
+ "SIGN",
+ "SIN",
+ "SINH",
+ "SQRT",
+ "TAN",
+ "TANH",
+ "TRUNC",
+ "WIDTH_BUCKET",
]
# Strings functions
string_functions = [
- 'CHR', 'CONCAT', 'INITCAP', 'LOWER', 'LPAD', 'LTRIM', 'NLS_INITCAP',
- 'NLS_LOWER', 'NLSSORT', 'NLS_UPPER', 'REGEXP_REPLACE', 'REGEXP_SUBSTR',
- 'REPLACE', 'RPAD', 'RTRIM', 'SOUNDEX', 'SUBSTR', 'TRANSLATE', 'TREAT',
- 'TRIM', 'UPPER', 'ASCII', 'INSTR', 'LENGTH', 'REGEXP_INSTR'
+ "CHR",
+ "CONCAT",
+ "INITCAP",
+ "LOWER",
+ "LPAD",
+ "LTRIM",
+ "NLS_INITCAP",
+ "NLS_LOWER",
+ "NLSSORT",
+ "NLS_UPPER",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "RPAD",
+ "RTRIM",
+ "SOUNDEX",
+ "SUBSTR",
+ "TRANSLATE",
+ "TREAT",
+ "TRIM",
+ "UPPER",
+ "ASCII",
+ "INSTR",
+ "LENGTH",
+ "REGEXP_INSTR",
]
# Aggregate functions
aggregate_functions = [
- 'AVG', 'COLLECT', 'CORR', 'COUNT', 'COVAR_POP', 'COVAR_SAMP', 'CUME_DIST',
- 'DENSE_RANK', 'FIRST', 'GROUP_ID', 'GROUPING', 'GROUPING_ID',
- 'LAST', 'MAX', 'MEDIAN', 'MIN', 'PERCENTILE_CONT',
- 'PERCENTILE_DISC', 'PERCENT_RANK', 'RANK',
- 'STATS_BINOMIAL_TEST', 'STATS_CROSSTAB', 'STATS_F_TEST',
- 'STATS_KS_TEST', 'STATS_MODE', 'STATS_MW_TEST',
- 'STATS_ONE_WAY_ANOVA', 'STATS_WSR_TEST', 'STDDEV',
- 'STDDEV_POP', 'STDDEV_SAMP', 'SUM', 'SYS_XMLAGG', 'VAR_POP',
- 'VAR_SAMP', 'VARIANCE', 'XMLAGG'
+ "AVG",
+ "COLLECT",
+ "CORR",
+ "COUNT",
+ "COVAR_POP",
+ "COVAR_SAMP",
+ "CUME_DIST",
+ "DENSE_RANK",
+ "FIRST",
+ "GROUP_ID",
+ "GROUPING",
+ "GROUPING_ID",
+ "LAST",
+ "MAX",
+ "MEDIAN",
+ "MIN",
+ "PERCENTILE_CONT",
+ "PERCENTILE_DISC",
+ "PERCENT_RANK",
+ "RANK",
+ "STATS_BINOMIAL_TEST",
+ "STATS_CROSSTAB",
+ "STATS_F_TEST",
+ "STATS_KS_TEST",
+ "STATS_MODE",
+ "STATS_MW_TEST",
+ "STATS_ONE_WAY_ANOVA",
+ "STATS_WSR_TEST",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "SUM",
+ "SYS_XMLAGG",
+ "VAR_POP",
+ "VAR_SAMP",
+ "VARIANCE",
+ "XMLAGG",
]
oracle_spatial_functions = [
# From http://docs.oracle.com/cd/B19306_01/appdev.102/b14255/toc.htm
# Spatial operators
- "SDO_ANYINTERACT", "SDO_CONTAINS", "SDO_COVEREDBY", "SDO_COVERS",
- "SDO_EQUAL", "SDO_FILTER", "SDO_INSIDE", "SDO_JOIN", "SDO_NN",
- "SDO_NN_DISTANCE", "SDO_ON", "SDO_OVERLAPBDYDISJOINT",
- "SDO_OVERLAPBDYINTERSECT", "SDO_OVERLAPS", "SDO_RELATE",
- "SDO_TOUCH", "SDO_WITHIN_DISTANCE",
+ "SDO_ANYINTERACT",
+ "SDO_CONTAINS",
+ "SDO_COVEREDBY",
+ "SDO_COVERS",
+ "SDO_EQUAL",
+ "SDO_FILTER",
+ "SDO_INSIDE",
+ "SDO_JOIN",
+ "SDO_NN",
+ "SDO_NN_DISTANCE",
+ "SDO_ON",
+ "SDO_OVERLAPBDYDISJOINT",
+ "SDO_OVERLAPBDYINTERSECT",
+ "SDO_OVERLAPS",
+ "SDO_RELATE",
+ "SDO_TOUCH",
+ "SDO_WITHIN_DISTANCE",
# SPATIAL AGGREGATE FUNCTIONS
- "SDO_AGGR_CENTROID", "SDO_AGGR_CONCAT_LINES",
- "SDO_AGGR_CONVEXHULL", "SDO_AGGR_LRS_CONCAT", "SDO_AGGR_MBR",
+ "SDO_AGGR_CENTROID",
+ "SDO_AGGR_CONCAT_LINES",
+ "SDO_AGGR_CONVEXHULL",
+ "SDO_AGGR_LRS_CONCAT",
+ "SDO_AGGR_MBR",
"SDO_AGGR_UNION",
# COORDINATE SYSTEM TRANSFORMATION (SDO_CS)
- "SDO_CS.ADD_PREFERENCE_FOR_OP", "SDO_CS.CONVERT_NADCON_TO_XML",
- "SDO_CS.CONVERT_NTV2_TO_XML", "SDO_CS.CONVERT_XML_TO_NADCON",
- "SDO_CS.CONVERT_XML_TO_NTV2", "SDO_CS.CREATE_CONCATENATED_OP",
+ "SDO_CS.ADD_PREFERENCE_FOR_OP",
+ "SDO_CS.CONVERT_NADCON_TO_XML",
+ "SDO_CS.CONVERT_NTV2_TO_XML",
+ "SDO_CS.CONVERT_XML_TO_NADCON",
+ "SDO_CS.CONVERT_XML_TO_NTV2",
+ "SDO_CS.CREATE_CONCATENATED_OP",
"SDO_CS.CREATE_OBVIOUS_EPSG_RULES",
"SDO_CS.CREATE_PREF_CONCATENATED_OP",
- "SDO_CS.DELETE_ALL_EPSG_RULES", "SDO_CS.DELETE_OP",
- "SDO_CS.DETERMINE_CHAIN", "SDO_CS.DETERMINE_DEFAULT_CHAIN",
- "SDO_CS.FIND_GEOG_CRS", "SDO_CS.FIND_PROJ_CRS",
- "SDO_CS.FROM_OGC_SIMPLEFEATURE_SRS", "SDO_CS.FROM_USNG",
+ "SDO_CS.DELETE_ALL_EPSG_RULES",
+ "SDO_CS.DELETE_OP",
+ "SDO_CS.DETERMINE_CHAIN",
+ "SDO_CS.DETERMINE_DEFAULT_CHAIN",
+ "SDO_CS.FIND_GEOG_CRS",
+ "SDO_CS.FIND_PROJ_CRS",
+ "SDO_CS.FROM_OGC_SIMPLEFEATURE_SRS",
+ "SDO_CS.FROM_USNG",
"SDO_CS.MAP_EPSG_SRID_TO_ORACLE",
"SDO_CS.MAP_ORACLE_SRID_TO_EPSG",
"SDO_CS.REVOKE_PREFERENCE_FOR_OP",
- "SDO_CS.TO_OGC_SIMPLEFEATURE_SRS", "SDO_CS.TO_USNG",
- "SDO_CS.TRANSFORM", "SDO_CS.TRANSFORM_LAYER",
+ "SDO_CS.TO_OGC_SIMPLEFEATURE_SRS",
+ "SDO_CS.TO_USNG",
+ "SDO_CS.TRANSFORM",
+ "SDO_CS.TRANSFORM_LAYER",
"SDO_CS.UPDATE_WKTS_FOR_ALL_EPSG_CRS",
"SDO_CS.UPDATE_WKTS_FOR_EPSG_CRS",
"SDO_CS.UPDATE_WKTS_FOR_EPSG_DATUM",
"SDO_CS.UPDATE_WKTS_FOR_EPSG_ELLIPS",
"SDO_CS.UPDATE_WKTS_FOR_EPSG_OP",
"SDO_CS.UPDATE_WKTS_FOR_EPSG_PARAM",
- "SDO_CS.UPDATE_WKTS_FOR_EPSG_PM", "SDO_CS.VALIDATE_WKT",
+ "SDO_CS.UPDATE_WKTS_FOR_EPSG_PM",
+ "SDO_CS.VALIDATE_WKT",
"SDO_CS.VIEWPORT_TRANSFORM",
# GEOCODING (SDO_GCDR)
- "SDO_GCDR.GEOCODE", "SDO_GCDR.GEOCODE_ADDR",
- "SDO_GCDR.GEOCODE_ADDR_ALL", "SDO_GCDR.GEOCODE_ALL",
- "SDO_GCDR.GEOCODE_AS_GEOMETRY", "SDO_GCDR.REVERSE_GEOCODE",
+ "SDO_GCDR.GEOCODE",
+ "SDO_GCDR.GEOCODE_ADDR",
+ "SDO_GCDR.GEOCODE_ADDR_ALL",
+ "SDO_GCDR.GEOCODE_ALL",
+ "SDO_GCDR.GEOCODE_AS_GEOMETRY",
+ "SDO_GCDR.REVERSE_GEOCODE",
# GEOMETRY (SDO_GEOM)
- "SDO_GEOM.RELATE", "SDO_GEOM.SDO_ARC_DENSIFY",
- "SDO_GEOM.SDO_AREA", "SDO_GEOM.SDO_BUFFER",
- "SDO_GEOM.SDO_CENTROID", "SDO_GEOM.SDO_CONVEXHULL",
- "SDO_GEOM.SDO_DIFFERENCE", "SDO_GEOM.SDO_DISTANCE",
- "SDO_GEOM.SDO_INTERSECTION", "SDO_GEOM.SDO_LENGTH",
- "SDO_GEOM.SDO_MAX_MBR_ORDINATE", "SDO_GEOM.SDO_MBR",
- "SDO_GEOM.SDO_MIN_MBR_ORDINATE", "SDO_GEOM.SDO_POINTONSURFACE",
- "SDO_GEOM.SDO_UNION", "SDO_GEOM.SDO_XOR",
+ "SDO_GEOM.RELATE",
+ "SDO_GEOM.SDO_ARC_DENSIFY",
+ "SDO_GEOM.SDO_AREA",
+ "SDO_GEOM.SDO_BUFFER",
+ "SDO_GEOM.SDO_CENTROID",
+ "SDO_GEOM.SDO_CONVEXHULL",
+ "SDO_GEOM.SDO_DIFFERENCE",
+ "SDO_GEOM.SDO_DISTANCE",
+ "SDO_GEOM.SDO_INTERSECTION",
+ "SDO_GEOM.SDO_LENGTH",
+ "SDO_GEOM.SDO_MAX_MBR_ORDINATE",
+ "SDO_GEOM.SDO_MBR",
+ "SDO_GEOM.SDO_MIN_MBR_ORDINATE",
+ "SDO_GEOM.SDO_POINTONSURFACE",
+ "SDO_GEOM.SDO_UNION",
+ "SDO_GEOM.SDO_XOR",
"SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT",
"SDO_GEOM.VALIDATE_LAYER_WITH_CONTEXT",
"SDO_GEOM.WITHIN_DISTANCE",
# LINEAR REFERENCING SYSTEM (SDO_LRS)
- "SDO_LRS.CLIP_GEOM_SEGMENT", "SDO_LRS.CONCATENATE_GEOM_SEGMENTS",
+ "SDO_LRS.CLIP_GEOM_SEGMENT",
+ "SDO_LRS.CONCATENATE_GEOM_SEGMENTS",
"SDO_LRS.CONNECTED_GEOM_SEGMENTS",
- "SDO_LRS.CONVERT_TO_LRS_DIM_ARRAY", "SDO_LRS.CONVERT_TO_LRS_GEOM",
+ "SDO_LRS.CONVERT_TO_LRS_DIM_ARRAY",
+ "SDO_LRS.CONVERT_TO_LRS_GEOM",
"SDO_LRS.CONVERT_TO_LRS_LAYER",
- "SDO_LRS.CONVERT_TO_STD_DIM_ARRAY", "SDO_LRS.CONVERT_TO_STD_GEOM",
- "SDO_LRS.CONVERT_TO_STD_LAYER", "SDO_LRS.DEFINE_GEOM_SEGMENT",
- "SDO_LRS.DYNAMIC_SEGMENT", "SDO_LRS.FIND_LRS_DIM_POS",
- "SDO_LRS.FIND_MEASURE", "SDO_LRS.FIND_OFFSET",
- "SDO_LRS.GEOM_SEGMENT_END_MEASURE", "SDO_LRS.GEOM_SEGMENT_END_PT",
+ "SDO_LRS.CONVERT_TO_STD_DIM_ARRAY",
+ "SDO_LRS.CONVERT_TO_STD_GEOM",
+ "SDO_LRS.CONVERT_TO_STD_LAYER",
+ "SDO_LRS.DEFINE_GEOM_SEGMENT",
+ "SDO_LRS.DYNAMIC_SEGMENT",
+ "SDO_LRS.FIND_LRS_DIM_POS",
+ "SDO_LRS.FIND_MEASURE",
+ "SDO_LRS.FIND_OFFSET",
+ "SDO_LRS.GEOM_SEGMENT_END_MEASURE",
+ "SDO_LRS.GEOM_SEGMENT_END_PT",
"SDO_LRS.GEOM_SEGMENT_LENGTH",
"SDO_LRS.GEOM_SEGMENT_START_MEASURE",
- "SDO_LRS.GEOM_SEGMENT_START_PT", "SDO_LRS.GET_MEASURE",
- "SDO_LRS.GET_NEXT_SHAPE_PT", "SDO_LRS.GET_NEXT_SHAPE_PT_MEASURE",
- "SDO_LRS.GET_PREV_SHAPE_PT", "SDO_LRS.GET_PREV_SHAPE_PT_MEASURE",
+ "SDO_LRS.GEOM_SEGMENT_START_PT",
+ "SDO_LRS.GET_MEASURE",
+ "SDO_LRS.GET_NEXT_SHAPE_PT",
+ "SDO_LRS.GET_NEXT_SHAPE_PT_MEASURE",
+ "SDO_LRS.GET_PREV_SHAPE_PT",
+ "SDO_LRS.GET_PREV_SHAPE_PT_MEASURE",
"SDO_LRS.IS_GEOM_SEGMENT_DEFINED",
- "SDO_LRS.IS_MEASURE_DECREASING", "SDO_LRS.IS_MEASURE_INCREASING",
- "SDO_LRS.IS_SHAPE_PT_MEASURE", "SDO_LRS.LOCATE_PT",
- "SDO_LRS.LRS_INTERSECTION", "SDO_LRS.MEASURE_RANGE",
- "SDO_LRS.MEASURE_TO_PERCENTAGE", "SDO_LRS.OFFSET_GEOM_SEGMENT",
- "SDO_LRS.PERCENTAGE_TO_MEASURE", "SDO_LRS.PROJECT_PT",
- "SDO_LRS.REDEFINE_GEOM_SEGMENT", "SDO_LRS.RESET_MEASURE",
- "SDO_LRS.REVERSE_GEOMETRY", "SDO_LRS.REVERSE_MEASURE",
- "SDO_LRS.SET_PT_MEASURE", "SDO_LRS.SPLIT_GEOM_SEGMENT",
- "SDO_LRS.TRANSLATE_MEASURE", "SDO_LRS.VALID_GEOM_SEGMENT",
- "SDO_LRS.VALID_LRS_PT", "SDO_LRS.VALID_MEASURE",
+ "SDO_LRS.IS_MEASURE_DECREASING",
+ "SDO_LRS.IS_MEASURE_INCREASING",
+ "SDO_LRS.IS_SHAPE_PT_MEASURE",
+ "SDO_LRS.LOCATE_PT",
+ "SDO_LRS.LRS_INTERSECTION",
+ "SDO_LRS.MEASURE_RANGE",
+ "SDO_LRS.MEASURE_TO_PERCENTAGE",
+ "SDO_LRS.OFFSET_GEOM_SEGMENT",
+ "SDO_LRS.PERCENTAGE_TO_MEASURE",
+ "SDO_LRS.PROJECT_PT",
+ "SDO_LRS.REDEFINE_GEOM_SEGMENT",
+ "SDO_LRS.RESET_MEASURE",
+ "SDO_LRS.REVERSE_GEOMETRY",
+ "SDO_LRS.REVERSE_MEASURE",
+ "SDO_LRS.SET_PT_MEASURE",
+ "SDO_LRS.SPLIT_GEOM_SEGMENT",
+ "SDO_LRS.TRANSLATE_MEASURE",
+ "SDO_LRS.VALID_GEOM_SEGMENT",
+ "SDO_LRS.VALID_LRS_PT",
+ "SDO_LRS.VALID_MEASURE",
"SDO_LRS.VALIDATE_LRS_GEOMETRY",
# SDO_MIGRATE
"SDO_MIGRATE.TO_CURRENT",
# SPATIAL ANALYSIS AND MINING (SDO_SAM)
- "SDO_SAM.AGGREGATES_FOR_GEOMETRY", "SDO_SAM.AGGREGATES_FOR_LAYER",
- "SDO_SAM.BIN_GEOMETRY", "SDO_SAM.BIN_LAYER",
+ "SDO_SAM.AGGREGATES_FOR_GEOMETRY",
+ "SDO_SAM.AGGREGATES_FOR_LAYER",
+ "SDO_SAM.BIN_GEOMETRY",
+ "SDO_SAM.BIN_LAYER",
"SDO_SAM.COLOCATED_REFERENCE_FEATURES",
- "SDO_SAM.SIMPLIFY_GEOMETRY", "SDO_SAM.SIMPLIFY_LAYER",
- "SDO_SAM.SPATIAL_CLUSTERS", "SDO_SAM.TILED_AGGREGATES",
+ "SDO_SAM.SIMPLIFY_GEOMETRY",
+ "SDO_SAM.SIMPLIFY_LAYER",
+ "SDO_SAM.SPATIAL_CLUSTERS",
+ "SDO_SAM.TILED_AGGREGATES",
"SDO_SAM.TILED_BINS",
# TUNING (SDO_TUNE)
- "SDO_TUNE.AVERAGE_MBR", "SDO_TUNE.ESTIMATE_RTREE_INDEX_SIZE",
- "SDO_TUNE.EXTENT_OF", "SDO_TUNE.MIX_INFO",
+ "SDO_TUNE.AVERAGE_MBR",
+ "SDO_TUNE.ESTIMATE_RTREE_INDEX_SIZE",
+ "SDO_TUNE.EXTENT_OF",
+ "SDO_TUNE.MIX_INFO",
"SDO_TUNE.QUALITY_DEGRADATION",
# UTILITY (SDO_UTIL)
- "SDO_UTIL.APPEND", "SDO_UTIL.CIRCLE_POLYGON",
- "SDO_UTIL.CONCAT_LINES", "SDO_UTIL.CONVERT_UNIT",
- "SDO_UTIL.ELLIPSE_POLYGON", "SDO_UTIL.EXTRACT",
- "SDO_UTIL.FROM_WKBGEOMETRY", "SDO_UTIL.FROM_WKTGEOMETRY",
- "SDO_UTIL.GETNUMELEM", "SDO_UTIL.GETNUMVERTICES",
- "SDO_UTIL.GETVERTICES", "SDO_UTIL.INITIALIZE_INDEXES_FOR_TTS",
- "SDO_UTIL.POINT_AT_BEARING", "SDO_UTIL.POLYGONTOLINE",
- "SDO_UTIL.PREPARE_FOR_TTS", "SDO_UTIL.RECTIFY_GEOMETRY",
+ "SDO_UTIL.APPEND",
+ "SDO_UTIL.CIRCLE_POLYGON",
+ "SDO_UTIL.CONCAT_LINES",
+ "SDO_UTIL.CONVERT_UNIT",
+ "SDO_UTIL.ELLIPSE_POLYGON",
+ "SDO_UTIL.EXTRACT",
+ "SDO_UTIL.FROM_WKBGEOMETRY",
+ "SDO_UTIL.FROM_WKTGEOMETRY",
+ "SDO_UTIL.GETNUMELEM",
+ "SDO_UTIL.GETNUMVERTICES",
+ "SDO_UTIL.GETVERTICES",
+ "SDO_UTIL.INITIALIZE_INDEXES_FOR_TTS",
+ "SDO_UTIL.POINT_AT_BEARING",
+ "SDO_UTIL.POLYGONTOLINE",
+ "SDO_UTIL.PREPARE_FOR_TTS",
+ "SDO_UTIL.RECTIFY_GEOMETRY",
"SDO_UTIL.REMOVE_DUPLICATE_VERTICES",
- "SDO_UTIL.REVERSE_LINESTRING", "SDO_UTIL.SIMPLIFY",
- "SDO_UTIL.TO_GMLGEOMETRY", "SDO_UTIL.TO_WKBGEOMETRY",
- "SDO_UTIL.TO_WKTGEOMETRY", "SDO_UTIL.VALIDATE_WKBGEOMETRY",
- "SDO_UTIL.VALIDATE_WKTGEOMETRY"
+ "SDO_UTIL.REVERSE_LINESTRING",
+ "SDO_UTIL.SIMPLIFY",
+ "SDO_UTIL.TO_GMLGEOMETRY",
+ "SDO_UTIL.TO_WKBGEOMETRY",
+ "SDO_UTIL.TO_WKTGEOMETRY",
+ "SDO_UTIL.VALIDATE_WKBGEOMETRY",
+ "SDO_UTIL.VALIDATE_WKTGEOMETRY",
]
# Oracle Operators
operators = [
- ' AND ', ' OR ', '||', ' < ', ' <= ', ' > ', ' >= ', ' = ',
- ' <> ', '!=', '^=', ' IS ', ' IS NOT ', ' IN ', ' ANY ', ' SOME ',
- ' NOT IN ', ' LIKE ', ' GLOB ', ' MATCH ', ' REGEXP ',
- ' BETWEEN x AND y ', ' NOT BETWEEN x AND y ', ' EXISTS ',
- ' IS NULL ', ' IS NOT NULL', ' ALL ', ' NOT ',
- ' CASE {column} WHEN {value} THEN {value} '
+ " AND ",
+ " OR ",
+ "||",
+ " < ",
+ " <= ",
+ " > ",
+ " >= ",
+ " = ",
+ " <> ",
+ "!=",
+ "^=",
+ " IS ",
+ " IS NOT ",
+ " IN ",
+ " ANY ",
+ " SOME ",
+ " NOT IN ",
+ " LIKE ",
+ " GLOB ",
+ " MATCH ",
+ " REGEXP ",
+ " BETWEEN x AND y ",
+ " NOT BETWEEN x AND y ",
+ " EXISTS ",
+ " IS NULL ",
+ " IS NOT NULL",
+ " ALL ",
+ " NOT ",
+ " CASE {column} WHEN {value} THEN {value} ",
]
# constants
@@ -278,26 +846,22 @@ def getSqlDictionary(spatial=True):
f += oracle_spatial_functions
c += oracle_spatial_constants
- return {'keyword': k, 'constant': c, 'function': f}
+ return {"keyword": k, "constant": c, "function": f}
def getQueryBuilderDictionary():
# concat functions
def ff(l):
- return [s for s in l if s[0] != '*']
+ return [s for s in l if s[0] != "*"]
def add_paren(l):
return [s + "(" for s in l]
foo = sorted(
- add_paren(
- ff(
- list(
- set.union(set(functions),
- set(oracle_spatial_functions))))))
+ add_paren(ff(list(set.union(set(functions), set(oracle_spatial_functions)))))
+ )
m = sorted(add_paren(ff(math_functions)))
agg = sorted(add_paren(ff(aggregate_functions)))
op = ff(operators)
s = sorted(add_paren(ff(string_functions)))
- return {'function': foo, 'math': m, 'aggregate': agg,
- 'operator': op, 'string': s}
+ return {"function": foo, "math": m, "aggregate": agg, "operator": op, "string": s}
diff --git a/python/plugins/db_manager/db_plugins/plugin.py b/python/plugins/db_manager/db_plugins/plugin.py
index 00d31a23119c..e5f39cd8bd3f 100644
--- a/python/plugins/db_manager/db_plugins/plugin.py
+++ b/python/plugins/db_manager/db_plugins/plugin.py
@@ -33,7 +33,7 @@
QInputDialog,
QMessageBox,
QDialog,
- QWidget
+ QWidget,
)
from qgis.PyQt.QtGui import QKeySequence
@@ -50,13 +50,10 @@
QgsRasterLayer,
QgsProject,
QgsMessageLog,
- QgsCoordinateReferenceSystem
+ QgsCoordinateReferenceSystem,
)
-from qgis.gui import (
- QgsMessageBarItem,
- QgsProjectionSelectionWidget
-)
+from qgis.gui import QgsMessageBarItem, QgsProjectionSelectionWidget
from ..db_plugins import createDbPlugin
@@ -66,12 +63,14 @@ class BaseError(Exception):
def __init__(self, e):
if isinstance(e, Exception):
- msg = e.args[0] if len(e.args) > 0 else ''
+ msg = e.args[0] if len(e.args) > 0 else ""
else:
msg = e
if not isinstance(msg, str):
- msg = str(msg, 'utf-8', 'replace') # convert from utf8 and replace errors (if any)
+ msg = str(
+ msg, "utf-8", "replace"
+ ) # convert from utf8 and replace errors (if any)
self.msg = msg
Exception.__init__(self, msg)
@@ -98,9 +97,13 @@ def __unicode__(self):
if self.query is None:
return BaseError.__unicode__(self)
- msg = QApplication.translate("DBManagerPlugin", "Error:\n{0}").format(BaseError.__unicode__(self))
+ msg = QApplication.translate("DBManagerPlugin", "Error:\n{0}").format(
+ BaseError.__unicode__(self)
+ )
if self.query:
- msg += QApplication.translate("DBManagerPlugin", "\n\nQuery:\n{0}").format(self.query)
+ msg += QApplication.translate("DBManagerPlugin", "\n\nQuery:\n{0}").format(
+ self.query
+ )
return msg
@@ -132,7 +135,7 @@ def info(self):
return DatabaseInfo(None)
def connect(self, parent=None):
- raise NotImplementedError('Needs to be implemented by subclasses')
+ raise NotImplementedError("Needs to be implemented by subclasses")
def connectToUri(self, uri):
self.db = self.databasesFactory(self, uri)
@@ -156,7 +159,9 @@ def remove(self):
md.deleteConnection(self.connectionName())
except (AttributeError, QgsProviderConnectionException):
settings = QgsSettings()
- settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), self.connectionName()))
+ settings.beginGroup(
+ f"/{self.connectionSettingsKey()}/{self.connectionName()}"
+ )
settings.remove("")
self.deleted.emit()
@@ -164,7 +169,7 @@ def remove(self):
@classmethod
def addConnection(self, conn_name, uri):
- raise NotImplementedError('Needs to be implemented by subclasses')
+ raise NotImplementedError("Needs to be implemented by subclasses")
@classmethod
def icon(self):
@@ -215,15 +220,19 @@ def databasesFactory(self, connection, uri):
@classmethod
def addConnectionActionSlot(self, item, action, parent):
- raise NotImplementedError('Needs to be implemented by subclasses')
+ raise NotImplementedError("Needs to be implemented by subclasses")
def removeActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
- res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
- QApplication.translate("DBManagerPlugin",
- "Really remove connection to {0}?").format(item.connectionName()),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ parent,
+ QApplication.translate("DBManagerPlugin", "DB Manager"),
+ QApplication.translate(
+ "DBManagerPlugin", "Really remove connection to {0}?"
+ ).format(item.connectionName()),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
finally:
@@ -310,22 +319,35 @@ def columnUniqueValuesModel(self, col, table, limit=10):
l = ""
if limit is not None:
l = "LIMIT %d" % limit
- return self.sqlResultModel("SELECT DISTINCT %s FROM %s %s" % (col, table, l), self)
+ return self.sqlResultModel(f"SELECT DISTINCT {col} FROM {table} {l}", self)
def uniqueIdFunction(self):
"""Return a SQL function used to generate a unique id for rows of a query"""
# may be overloaded by derived classes
return "row_number() over ()"
- def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, filter=""):
+ def toSqlLayer(
+ self,
+ sql,
+ geomCol,
+ uniqueCol,
+ layerName="QueryLayer",
+ layerType=None,
+ avoidSelectById=False,
+ filter="",
+ ):
if uniqueCol is None:
- if hasattr(self, 'uniqueIdFunction'):
+ if hasattr(self, "uniqueIdFunction"):
uniqueFct = self.uniqueIdFunction()
if uniqueFct is not None:
q = 1
while "_subq_%d_" % q in sql:
q += 1
- sql = "SELECT %s AS _uid_,* FROM (%s\n) AS _subq_%d_" % (uniqueFct, sql, q)
+ sql = "SELECT %s AS _uid_,* FROM (%s\n) AS _subq_%d_" % (
+ uniqueFct,
+ sql,
+ q,
+ )
uniqueCol = "_uid_"
uri = self.uri()
@@ -352,45 +374,91 @@ def registerSubPluginActions(self, mainWindow):
def registerDatabaseActions(self, mainWindow):
action = QAction(QApplication.translate("DBManagerPlugin", "&Re-connect"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Database"),
- self.reconnectActionSlot)
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Database"),
+ self.reconnectActionSlot,
+ )
if self.schemas() is not None:
- action = QAction(QApplication.translate("DBManagerPlugin", "&Create Schema…"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Schema"),
- self.createSchemaActionSlot)
- action = QAction(QApplication.translate("DBManagerPlugin", "&Delete (Empty) Schema"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Schema"),
- self.deleteSchemaActionSlot)
-
- action = QAction(QApplication.translate("DBManagerPlugin", "Delete Selected Item"), self)
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Create Schema…"), self
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Schema"),
+ self.createSchemaActionSlot,
+ )
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Delete (Empty) Schema"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Schema"),
+ self.deleteSchemaActionSlot,
+ )
+
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "Delete Selected Item"), self
+ )
mainWindow.registerAction(action, None, self.deleteActionSlot)
action.setShortcuts(QKeySequence.StandardKey.Delete)
- action = QAction(QgsApplication.getThemeIcon("/mActionCreateTable.svg"),
- QApplication.translate("DBManagerPlugin", "&Create Table…"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
- self.createTableActionSlot)
- action = QAction(QgsApplication.getThemeIcon("/mActionEditTable.svg"),
- QApplication.translate("DBManagerPlugin", "&Edit Table…"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"), self.editTableActionSlot)
- action = QAction(QgsApplication.getThemeIcon("/mActionDeleteTable.svg"),
- QApplication.translate("DBManagerPlugin", "&Delete Table/View…"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
- self.deleteTableActionSlot)
- action = QAction(QApplication.translate("DBManagerPlugin", "&Empty Table…"), self)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
- self.emptyTableActionSlot)
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionCreateTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Create Table…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.createTableActionSlot,
+ )
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionEditTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Edit Table…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.editTableActionSlot,
+ )
+ action = QAction(
+ QgsApplication.getThemeIcon("/mActionDeleteTable.svg"),
+ QApplication.translate("DBManagerPlugin", "&Delete Table/View…"),
+ self,
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.deleteTableActionSlot,
+ )
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Empty Table…"), self
+ )
+ mainWindow.registerAction(
+ action,
+ QApplication.translate("DBManagerPlugin", "&Table"),
+ self.emptyTableActionSlot,
+ )
if self.schemas() is not None:
- action = QAction(QApplication.translate("DBManagerPlugin", "&Move to Schema"), self)
+ action = QAction(
+ QApplication.translate("DBManagerPlugin", "&Move to Schema"), self
+ )
action.setMenu(QMenu(mainWindow))
def invoke_callback():
- return mainWindow.invokeCallback(self.prepareMenuMoveTableToSchemaActionSlot)
+ return mainWindow.invokeCallback(
+ self.prepareMenuMoveTableToSchemaActionSlot
+ )
action.menu().aboutToShow.connect(invoke_callback)
- mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"))
+ mainWindow.registerAction(
+ action, QApplication.translate("DBManagerPlugin", "&Table")
+ )
def reconnectActionSlot(self, item, action, parent):
db = item.database()
@@ -404,20 +472,36 @@ def deleteActionSlot(self, item, action, parent):
self.deleteTableActionSlot(item, action, parent)
else:
QApplication.restoreOverrideCursor()
- parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Cannot delete the selected item."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ QApplication.translate(
+ "DBManagerPlugin", "Cannot delete the selected item."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
def createSchemaActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
- if not isinstance(item, (DBPlugin, Schema, Table)) or item.database() is None:
+ if (
+ not isinstance(item, (DBPlugin, Schema, Table))
+ or item.database() is None
+ ):
parent.infoBar.pushMessage(
- QApplication.translate("DBManagerPlugin", "No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ QApplication.translate(
+ "DBManagerPlugin",
+ "No database selected or you are not connected to it.",
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
- (schema, ok) = QInputDialog.getText(parent, QApplication.translate("DBManagerPlugin", "New schema"),
- QApplication.translate("DBManagerPlugin", "Enter new schema name"))
+ (schema, ok) = QInputDialog.getText(
+ parent,
+ QApplication.translate("DBManagerPlugin", "New schema"),
+ QApplication.translate("DBManagerPlugin", "Enter new schema name"),
+ )
if not ok:
return
finally:
@@ -430,13 +514,21 @@ def deleteSchemaActionSlot(self, item, action, parent):
try:
if not isinstance(item, Schema):
parent.infoBar.pushMessage(
- QApplication.translate("DBManagerPlugin", "Select an empty schema for deletion."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ QApplication.translate(
+ "DBManagerPlugin", "Select an empty schema for deletion."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
- res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
- QApplication.translate("DBManagerPlugin",
- "Really delete schema {0}?").format(item.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ parent,
+ QApplication.translate("DBManagerPlugin", "DB Manager"),
+ QApplication.translate(
+ "DBManagerPlugin", "Really delete schema {0}?"
+ ).format(item.name),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
finally:
@@ -459,10 +551,15 @@ def createSchema(self, name):
def createTableActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
- if not hasattr(item, 'database') or item.database() is None:
+ if not hasattr(item, "database") or item.database() is None:
parent.infoBar.pushMessage(
- QApplication.translate("DBManagerPlugin", "No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ QApplication.translate(
+ "DBManagerPlugin",
+ "No database selected or you are not connected to it.",
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
from ..dlg_create_table import DlgCreateTable
@@ -473,13 +570,23 @@ def editTableActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, Table) or item.isView:
- parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table to edit."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ QApplication.translate(
+ "DBManagerPlugin", "Select a table to edit."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
if isinstance(item, RasterTable):
- parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Editing of raster tables is not supported."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ QApplication.translate(
+ "DBManagerPlugin", "Editing of raster tables is not supported."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
from ..dlg_table_properties import DlgTableProperties
@@ -493,13 +600,21 @@ def deleteTableActionSlot(self, item, action, parent):
try:
if not isinstance(item, Table):
parent.infoBar.pushMessage(
- QApplication.translate("DBManagerPlugin", "Select a table/view for deletion."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ QApplication.translate(
+ "DBManagerPlugin", "Select a table/view for deletion."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
- res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
- QApplication.translate("DBManagerPlugin",
- "Really delete table/view {0}?").format(item.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ parent,
+ QApplication.translate("DBManagerPlugin", "DB Manager"),
+ QApplication.translate(
+ "DBManagerPlugin", "Really delete table/view {0}?"
+ ).format(item.name),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
finally:
@@ -511,13 +626,22 @@ def emptyTableActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, Table) or item.isView:
- parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table to empty it."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ QApplication.translate(
+ "DBManagerPlugin", "Select a table to empty it."
+ ),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
- res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
- QApplication.translate("DBManagerPlugin",
- "Really delete all items from table {0}?").format(item.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ parent,
+ QApplication.translate("DBManagerPlugin", "DB Manager"),
+ QApplication.translate(
+ "DBManagerPlugin", "Really delete all items from table {0}?"
+ ).format(item.name),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
finally:
@@ -526,10 +650,12 @@ def emptyTableActionSlot(self, item, action, parent):
item.empty()
def prepareMenuMoveTableToSchemaActionSlot(self, item, menu, mainWindow):
- """ populate menu with schemas """
+ """populate menu with schemas"""
def slot(x):
- return lambda: mainWindow.invokeCallback(self.moveTableToSchemaActionSlot, x)
+ return lambda: mainWindow.invokeCallback(
+ self.moveTableToSchemaActionSlot, x
+ )
menu.clear()
for schema in self.schemas():
@@ -539,8 +665,11 @@ def moveTableToSchemaActionSlot(self, item, action, parent, new_schema):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, Table):
- parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table/view."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ QApplication.translate("DBManagerPlugin", "Select a table/view."),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -567,10 +696,7 @@ def rasterTablesFactory(self, row, db, schema=None):
def tables(self, schema=None, sys_tables=False):
tables = self.connector.getTables(schema.name if schema else None, sys_tables)
if tables is not None:
- ret = [
- self.tablesFactory(t, self, schema)
- for t in tables
- ]
+ ret = [self.tablesFactory(t, self, schema) for t in tables]
return ret
def createTable(self, table, fields, schema=None):
@@ -601,7 +727,9 @@ def createVectorTable(self, table, fields, geom, schema=None):
geomCol, geomType, geomSrid, geomDim = geom[:4]
createSpatialIndex = geom[4] if len(geom) > 4 else False
- self.connector.addGeometryColumn((schema, table), geomCol, geomType, geomSrid, geomDim)
+ self.connector.addGeometryColumn(
+ (schema, table), geomCol, geomType, geomSrid, geomDim
+ )
if createSpatialIndex:
# commit data definition changes, otherwise index can't be built
@@ -670,7 +798,7 @@ class Table(DbItemObject):
def __init__(self, db, schema=None, parent=None):
DbItemObject.__init__(self, db)
self._schema = schema
- if hasattr(self, 'type'):
+ if hasattr(self, "type"):
return
self.type = Table.TableType
@@ -678,7 +806,9 @@ def __init__(self, db, schema=None, parent=None):
self.comment = None
self.rowCount = None
- self._fields = self._indexes = self._constraints = self._triggers = self._rules = None
+ self._fields = self._indexes = self._constraints = self._triggers = (
+ self._rules
+ ) = None
def __del__(self):
pass # print "Table.__del__", self
@@ -710,7 +840,9 @@ def delete(self):
def rename(self, new_name):
self.aboutToChange.emit()
- ret = self.database().connector.renameTable((self.schemaName(), self.name), new_name)
+ ret = self.database().connector.renameTable(
+ (self.schemaName(), self.name), new_name
+ )
if ret is not False:
self.name = new_name
self._triggers = None
@@ -730,7 +862,9 @@ def moveToSchema(self, schema):
self.aboutToChange.emit()
if self.schema() == schema:
return True
- ret = self.database().connector.moveTableToSchema((self.schemaName(), self.name), schema.name)
+ ret = self.database().connector.moveTableToSchema(
+ (self.schemaName(), self.name), schema.name
+ )
if ret is not False:
self.schema().refresh()
schema.refresh()
@@ -743,10 +877,18 @@ def info(self):
def uri(self):
uri = self.database().uri()
- schema = self.schemaName() if self.schemaName() else ''
- geomCol = self.geomColumn if self.type in [Table.VectorType, Table.RasterType] else ""
+ schema = self.schemaName() if self.schemaName() else ""
+ geomCol = (
+ self.geomColumn if self.type in [Table.VectorType, Table.RasterType] else ""
+ )
uniqueCol = self.getValidQgisUniqueFields(True) if self.isView else None
- uri.setDataSource(schema, self.name, geomCol if geomCol else None, None, uniqueCol.name if uniqueCol else "")
+ uri.setDataSource(
+ schema,
+ self.name,
+ geomCol if geomCol else None,
+ None,
+ uniqueCol.name if uniqueCol else "",
+ )
return uri
def crs(self):
@@ -756,7 +898,12 @@ def crs(self):
def mimeUri(self):
layerType = "raster" if self.type == Table.RasterType else "vector"
- return "%s:%s:%s:%s" % (layerType, self.database().dbplugin().providerName(), self.name, self.uri().uri(False))
+ return "{}:{}:{}:{}".format(
+ layerType,
+ self.database().dbplugin().providerName(),
+ self.name,
+ self.uri().uri(False),
+ )
def toMapLayer(self, geometryType=None, crs=None):
provider = self.database().dbplugin().providerName()
@@ -776,9 +923,9 @@ def geometryType(self):
pass
def getValidQgisUniqueFields(self, onlyOne=False):
- """ list of fields valid to load the table as layer in QGIS canvas.
- QGIS automatically search for a valid unique field, so it's
- needed only for queries and views """
+ """list of fields valid to load the table as layer in QGIS canvas.
+ QGIS automatically search for a valid unique field, so it's
+ needed only for queries and views"""
ret = []
@@ -793,7 +940,10 @@ def getValidQgisUniqueFields(self, onlyOne=False):
for idx in indexes:
if idx.isUnique and len(idx.columns) == 1:
fld = idx.fields()[idx.columns[0]]
- if fld.dataType in ["oid", "serial", "int4", "int8"] and fld not in ret:
+ if (
+ fld.dataType in ["oid", "serial", "int4", "int8"]
+ and fld not in ret
+ ):
ret.append(fld)
# and finally append the other suitable fields
@@ -809,11 +959,13 @@ def tableDataModel(self, parent):
pass
def tableFieldsFactory(self, row, table):
- raise NotImplementedError('Needs to be implemented by subclasses')
+ raise NotImplementedError("Needs to be implemented by subclasses")
def fields(self):
if self._fields is None:
- fields = self.database().connector.getTableFields((self.schemaName(), self.name))
+ fields = self.database().connector.getTableFields(
+ (self.schemaName(), self.name)
+ )
if fields is not None:
self._fields = [self.tableFieldsFactory(x, self) for x in fields]
return self._fields
@@ -824,14 +976,18 @@ def refreshFields(self):
def addField(self, fld):
self.aboutToChange.emit()
- ret = self.database().connector.addTableColumn((self.schemaName(), self.name), fld.definition())
+ ret = self.database().connector.addTableColumn(
+ (self.schemaName(), self.name), fld.definition()
+ )
if ret is not False:
self.refreshFields()
return ret
def deleteField(self, fld):
self.aboutToChange.emit()
- ret = self.database().connector.deleteTableColumn((self.schemaName(), self.name), fld.name)
+ ret = self.database().connector.deleteTableColumn(
+ (self.schemaName(), self.name), fld.name
+ )
if ret is not False:
self.refreshFields()
self.refreshConstraints()
@@ -840,7 +996,9 @@ def deleteField(self, fld):
def addGeometryColumn(self, geomCol, geomType, srid, dim, createSpatialIndex=False):
self.aboutToChange.emit()
- ret = self.database().connector.addGeometryColumn((self.schemaName(), self.name), geomCol, geomType, srid, dim)
+ ret = self.database().connector.addGeometryColumn(
+ (self.schemaName(), self.name), geomCol, geomType, srid, dim
+ )
if not ret:
return False
@@ -848,10 +1006,14 @@ def addGeometryColumn(self, geomCol, geomType, srid, dim, createSpatialIndex=Fal
if createSpatialIndex:
# commit data definition changes, otherwise index can't be built
self.database().connector._commit()
- self.database().connector.createSpatialIndex((self.schemaName(), self.name), geomCol)
+ self.database().connector.createSpatialIndex(
+ (self.schemaName(), self.name), geomCol
+ )
finally:
- self.schema().refresh() if self.schema() else self.database().refresh() # another table was added
+ (
+ self.schema().refresh() if self.schema() else self.database().refresh()
+ ) # another table was added
return True
def tableConstraintsFactory(self):
@@ -859,9 +1021,13 @@ def tableConstraintsFactory(self):
def constraints(self):
if self._constraints is None:
- constraints = self.database().connector.getTableConstraints((self.schemaName(), self.name))
+ constraints = self.database().connector.getTableConstraints(
+ (self.schemaName(), self.name)
+ )
if constraints is not None:
- self._constraints = [self.tableConstraintsFactory(x, self) for x in constraints]
+ self._constraints = [
+ self.tableConstraintsFactory(x, self) for x in constraints
+ ]
return self._constraints
def refreshConstraints(self):
@@ -871,11 +1037,13 @@ def refreshConstraints(self):
def addConstraint(self, constr):
self.aboutToChange.emit()
if constr.type == TableConstraint.TypePrimaryKey:
- ret = self.database().connector.addTablePrimaryKey((self.schemaName(), self.name),
- constr.fields()[constr.columns[0]].name)
+ ret = self.database().connector.addTablePrimaryKey(
+ (self.schemaName(), self.name), constr.fields()[constr.columns[0]].name
+ )
elif constr.type == TableConstraint.TypeUnique:
- ret = self.database().connector.addTableUniqueConstraint((self.schemaName(), self.name),
- constr.fields()[constr.columns[0]].name)
+ ret = self.database().connector.addTableUniqueConstraint(
+ (self.schemaName(), self.name), constr.fields()[constr.columns[0]].name
+ )
else:
return False
if ret is not False:
@@ -884,7 +1052,9 @@ def addConstraint(self, constr):
def deleteConstraint(self, constr):
self.aboutToChange.emit()
- ret = self.database().connector.deleteTableConstraint((self.schemaName(), self.name), constr.name)
+ ret = self.database().connector.deleteTableConstraint(
+ (self.schemaName(), self.name), constr.name
+ )
if ret is not False:
self.refreshConstraints()
return ret
@@ -894,7 +1064,9 @@ def tableIndexesFactory(self):
def indexes(self):
if self._indexes is None:
- indexes = self.database().connector.getTableIndexes((self.schemaName(), self.name))
+ indexes = self.database().connector.getTableIndexes(
+ (self.schemaName(), self.name)
+ )
if indexes is not None:
self._indexes = [self.tableIndexesFactory(x, self) for x in indexes]
return self._indexes
@@ -905,15 +1077,18 @@ def refreshIndexes(self):
def addIndex(self, idx):
self.aboutToChange.emit()
- ret = self.database().connector.createTableIndex((self.schemaName(), self.name), idx.name,
- idx.fields()[idx.columns[0]].name)
+ ret = self.database().connector.createTableIndex(
+ (self.schemaName(), self.name), idx.name, idx.fields()[idx.columns[0]].name
+ )
if ret is not False:
self.refreshIndexes()
return ret
def deleteIndex(self, idx):
self.aboutToChange.emit()
- ret = self.database().connector.deleteTableIndex((self.schemaName(), self.name), idx.name)
+ ret = self.database().connector.deleteTableIndex(
+ (self.schemaName(), self.name), idx.name
+ )
if ret is not False:
self.refreshIndexes()
return ret
@@ -923,7 +1098,9 @@ def tableTriggersFactory(self, row, table):
def triggers(self):
if self._triggers is None:
- triggers = self.database().connector.getTableTriggers((self.schemaName(), self.name))
+ triggers = self.database().connector.getTableTriggers(
+ (self.schemaName(), self.name)
+ )
if triggers is not None:
self._triggers = [self.tableTriggersFactory(x, self) for x in triggers]
return self._triggers
@@ -937,7 +1114,9 @@ def tableRulesFactory(self, row, table):
def rules(self):
if self._rules is None:
- rules = self.database().connector.getTableRules((self.schemaName(), self.name))
+ rules = self.database().connector.getTableRules(
+ (self.schemaName(), self.name)
+ )
if rules is not None:
self._rules = [self.tableRulesFactory(x, self) for x in rules]
return self._rules
@@ -950,7 +1129,9 @@ def refreshRowCount(self):
self.aboutToChange.emit()
prevRowCount = self.rowCount
try:
- self.rowCount = self.database().connector.getTableRowCount((self.schemaName(), self.name))
+ self.rowCount = self.database().connector.getTableRowCount(
+ (self.schemaName(), self.name)
+ )
self.rowCount = int(self.rowCount) if self.rowCount is not None else None
except DbError:
self.rowCount = None
@@ -966,14 +1147,23 @@ def runAction(self, action):
return True
elif action.startswith("triggers/"):
- parts = action.split('/')
+ parts = action.split("/")
trigger_action = parts[1]
- msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} all triggers?").format(trigger_action)
+ msg = QApplication.translate(
+ "DBManagerPlugin", "Do you want to {0} all triggers?"
+ ).format(trigger_action)
QApplication.restoreOverrideCursor()
try:
- if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Table triggers"), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ if (
+ QMessageBox.question(
+ None,
+ QApplication.translate("DBManagerPlugin", "Table triggers"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return False
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -981,35 +1171,49 @@ def runAction(self, action):
if trigger_action == "enable" or trigger_action == "disable":
enable = trigger_action == "enable"
self.aboutToChange.emit()
- self.database().connector.enableAllTableTriggers(enable, (self.schemaName(), self.name))
+ self.database().connector.enableAllTableTriggers(
+ enable, (self.schemaName(), self.name)
+ )
self.refreshTriggers()
return True
elif action.startswith("trigger/"):
- parts = action.split('/')
+ parts = action.split("/")
trigger_name = parts[1]
trigger_action = parts[2]
- msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} trigger {1}?").format(
- trigger_action, trigger_name)
+ msg = QApplication.translate(
+ "DBManagerPlugin", "Do you want to {0} trigger {1}?"
+ ).format(trigger_action, trigger_name)
QApplication.restoreOverrideCursor()
try:
- if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Table trigger"), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ if (
+ QMessageBox.question(
+ None,
+ QApplication.translate("DBManagerPlugin", "Table trigger"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return False
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
if trigger_action == "delete":
self.aboutToChange.emit()
- self.database().connector.deleteTableTrigger(trigger_name, (self.schemaName(), self.name))
+ self.database().connector.deleteTableTrigger(
+ trigger_name, (self.schemaName(), self.name)
+ )
self.refreshTriggers()
return True
elif trigger_action == "enable" or trigger_action == "disable":
enable = trigger_action == "enable"
self.aboutToChange.emit()
- self.database().connector.enableTableTrigger(trigger_name, enable, (self.schemaName(), self.name))
+ self.database().connector.enableTableTrigger(
+ trigger_name, enable, (self.schemaName(), self.name)
+ )
self.refreshTriggers()
return True
@@ -1023,7 +1227,9 @@ def addExtraContextMenuEntries(self, menu):
class VectorTable(Table):
def __init__(self, db, schema=None, parent=None):
- if not hasattr(self, 'type'): # check if the superclass constructor was called yet!
+ if not hasattr(
+ self, "type"
+ ): # check if the superclass constructor was called yet!
Table.__init__(self, db, schema, parent)
self.type = Table.VectorType
self.geomColumn = self.geomType = self.geomDim = self.srid = None
@@ -1060,7 +1266,9 @@ def hasSpatialIndex(self, geom_column=None):
def createSpatialIndex(self, geom_column=None):
self.aboutToChange.emit()
geom_column = geom_column if geom_column is not None else self.geomColumn
- ret = self.database().connector.createSpatialIndex((self.schemaName(), self.name), geom_column)
+ ret = self.database().connector.createSpatialIndex(
+ (self.schemaName(), self.name), geom_column
+ )
if ret is not False:
self.refreshIndexes()
return ret
@@ -1068,7 +1276,9 @@ def createSpatialIndex(self, geom_column=None):
def deleteSpatialIndex(self, geom_column=None):
self.aboutToChange.emit()
geom_column = geom_column if geom_column is not None else self.geomColumn
- ret = self.database().connector.deleteSpatialIndex((self.schemaName(), self.name), geom_column)
+ ret = self.database().connector.deleteSpatialIndex(
+ (self.schemaName(), self.name), geom_column
+ )
if ret is not False:
self.refreshIndexes()
return ret
@@ -1076,7 +1286,9 @@ def deleteSpatialIndex(self, geom_column=None):
def refreshTableExtent(self):
prevExtent = self.extent
try:
- self.extent = self.database().connector.getTableExtent((self.schemaName(), self.name), self.geomColumn)
+ self.extent = self.database().connector.getTableExtent(
+ (self.schemaName(), self.name), self.geomColumn
+ )
except DbError:
self.extent = None
if self.extent != prevExtent:
@@ -1085,8 +1297,9 @@ def refreshTableExtent(self):
def refreshTableEstimatedExtent(self):
prevEstimatedExtent = self.estimatedExtent
try:
- self.estimatedExtent = self.database().connector.getTableEstimatedExtent((self.schemaName(), self.name),
- self.geomColumn)
+ self.estimatedExtent = self.database().connector.getTableEstimatedExtent(
+ (self.schemaName(), self.name), self.geomColumn
+ )
except DbError:
self.estimatedExtent = None
if self.estimatedExtent != prevEstimatedExtent:
@@ -1096,15 +1309,23 @@ def runAction(self, action):
action = str(action)
if action.startswith("spatialindex/"):
- parts = action.split('/')
+ parts = action.split("/")
spatialIndex_action = parts[1]
- msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} spatial index for field {1}?").format(
- spatialIndex_action, self.geomColumn)
+ msg = QApplication.translate(
+ "DBManagerPlugin", "Do you want to {0} spatial index for field {1}?"
+ ).format(spatialIndex_action, self.geomColumn)
QApplication.restoreOverrideCursor()
try:
- if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Spatial Index"), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ if (
+ QMessageBox.question(
+ None,
+ QApplication.translate("DBManagerPlugin", "Spatial Index"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return False
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -1131,48 +1352,67 @@ def addLayer(self, geometryType=None, crs=None):
layer = self.toMapLayer(geometryType, crs)
layers = QgsProject.instance().addMapLayers([layer])
if len(layers) != 1:
- QgsMessageLog.logMessage(self.tr("{layer} is an invalid layer - not loaded").format(layer=layer.publicSource()))
- msgLabel = QLabel(self.tr("{layer} is an invalid layer and cannot be loaded. Please check the message log for further info.").format(layer=layer.publicSource()), self.mainWindow.infoBar)
+ QgsMessageLog.logMessage(
+ self.tr("{layer} is an invalid layer - not loaded").format(
+ layer=layer.publicSource()
+ )
+ )
+ msgLabel = QLabel(
+ self.tr(
+ '{layer} is an invalid layer and cannot be loaded. Please check the message log for further info.'
+ ).format(layer=layer.publicSource()),
+ self.mainWindow.infoBar,
+ )
msgLabel.setWordWrap(True)
- msgLabel.linkActivated.connect(self.mainWindow.iface.mainWindow().findChild(QWidget, "MessageLog").show)
+ msgLabel.linkActivated.connect(
+ self.mainWindow.iface.mainWindow().findChild(QWidget, "MessageLog").show
+ )
msgLabel.linkActivated.connect(self.mainWindow.iface.mainWindow().raise_)
- self.mainWindow.infoBar.pushItem(QgsMessageBarItem(msgLabel, Qgis.MessageLevel.Warning))
+ self.mainWindow.infoBar.pushItem(
+ QgsMessageBarItem(msgLabel, Qgis.MessageLevel.Warning)
+ )
def showAdvancedVectorDialog(self):
dlg = QDialog()
- dlg.setObjectName('dbManagerAdvancedVectorDialog')
+ dlg.setObjectName("dbManagerAdvancedVectorDialog")
settings = QgsSettings()
- dlg.restoreGeometry(settings.value("/DB_Manager/advancedAddDialog/geometry", QByteArray(), type=QByteArray))
+ dlg.restoreGeometry(
+ settings.value(
+ "/DB_Manager/advancedAddDialog/geometry", QByteArray(), type=QByteArray
+ )
+ )
layout = QFormLayout()
dlg.setLayout(layout)
- dlg.setWindowTitle(self.tr('Add Layer {}').format(self.name))
+ dlg.setWindowTitle(self.tr("Add Layer {}").format(self.name))
geometryTypeComboBox = QComboBox()
- geometryTypeComboBox.addItem(self.tr('Point'), 'POINT')
- geometryTypeComboBox.addItem(self.tr('Line'), 'LINESTRING')
- geometryTypeComboBox.addItem(self.tr('Polygon'), 'POLYGON')
- layout.addRow(self.tr('Geometry Type'), geometryTypeComboBox)
- zCheckBox = QCheckBox(self.tr('With Z'))
- mCheckBox = QCheckBox(self.tr('With M'))
+ geometryTypeComboBox.addItem(self.tr("Point"), "POINT")
+ geometryTypeComboBox.addItem(self.tr("Line"), "LINESTRING")
+ geometryTypeComboBox.addItem(self.tr("Polygon"), "POLYGON")
+ layout.addRow(self.tr("Geometry Type"), geometryTypeComboBox)
+ zCheckBox = QCheckBox(self.tr("With Z"))
+ mCheckBox = QCheckBox(self.tr("With M"))
layout.addRow(zCheckBox)
layout.addRow(mCheckBox)
crsSelector = QgsProjectionSelectionWidget()
crsSelector.setCrs(self.crs())
- layout.addRow(self.tr('CRS'), crsSelector)
+ layout.addRow(self.tr("CRS"), crsSelector)
def selectedGeometryType():
geomType = geometryTypeComboBox.currentData()
if zCheckBox.isChecked():
- geomType += 'Z'
+ geomType += "Z"
if mCheckBox.isChecked():
- geomType += 'M'
+ geomType += "M"
return geomType
def selectedCrs():
return crsSelector.crs()
- addButton = QPushButton(self.tr('Load Layer'))
- addButton.clicked.connect(lambda: self.addLayer(selectedGeometryType(), selectedCrs()))
+ addButton = QPushButton(self.tr("Load Layer"))
+ addButton.clicked.connect(
+ lambda: self.addLayer(selectedGeometryType(), selectedCrs())
+ )
btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel)
btns.addButton(addButton, QDialogButtonBox.ButtonRole.ActionRole)
@@ -1190,17 +1430,24 @@ def selectedCrs():
def addExtraContextMenuEntries(self, menu):
"""Called whenever a context menu is shown for this table. Can be used to add additional actions to the menu."""
- if self.geomType == 'GEOMETRY':
- menu.addAction(QApplication.translate("DBManagerPlugin", "Add Layer (Advanced)…"), self.showAdvancedVectorDialog)
+ if self.geomType == "GEOMETRY":
+ menu.addAction(
+ QApplication.translate("DBManagerPlugin", "Add Layer (Advanced)…"),
+ self.showAdvancedVectorDialog,
+ )
class RasterTable(Table):
def __init__(self, db, schema=None, parent=None):
- if not hasattr(self, 'type'): # check if the superclass constructor was called yet!
+ if not hasattr(
+ self, "type"
+ ): # check if the superclass constructor was called yet!
Table.__init__(self, db, schema, parent)
self.type = Table.RasterType
- self.geomColumn = self.geomType = self.pixelSizeX = self.pixelSizeY = self.pixelType = self.isExternal = self.srid = None
+ self.geomColumn = self.geomType = self.pixelSizeX = self.pixelSizeY = (
+ self.pixelType
+ ) = self.isExternal = self.srid = None
self.extent = None
def info(self):
@@ -1225,35 +1472,41 @@ class TableField(TableSubItemObject):
def __init__(self, table):
TableSubItemObject.__init__(self, table)
- self.num = self.name = self.dataType = self.modifier = self.notNull = self.default = self.hasDefault = self.primaryKey = None
+ self.num = self.name = self.dataType = self.modifier = self.notNull = (
+ self.default
+ ) = self.hasDefault = self.primaryKey = None
self.comment = None
def type2String(self):
if self.modifier is None or self.modifier == -1:
return "%s" % self.dataType
- return "%s (%s)" % (self.dataType, self.modifier)
+ return f"{self.dataType} ({self.modifier})"
def default2String(self):
if not self.hasDefault:
- return ''
+ return ""
return self.default if self.default is not None else "NULL"
def definition(self):
from .connector import DBConnector
- quoteIdFunc = self.database().connector.quoteId if self.database() else DBConnector.quoteId
+ quoteIdFunc = (
+ self.database().connector.quoteId
+ if self.database()
+ else DBConnector.quoteId
+ )
name = quoteIdFunc(self.name)
not_null = "NOT NULL" if self.notNull else ""
- txt = "%s %s %s" % (name, self.type2String(), not_null)
+ txt = f"{name} {self.type2String()} {not_null}"
if self.hasDefault:
txt += " DEFAULT %s" % self.default2String()
return txt
def getComment(self):
"""Returns the comment for a field"""
- return ''
+ return ""
def delete(self):
return self.table().deleteField(self)
@@ -1261,7 +1514,14 @@ def delete(self):
def rename(self, new_name):
return self.update(new_name)
- def update(self, new_name, new_type_str=None, new_not_null=None, new_default_str=None, new_comment=None):
+ def update(
+ self,
+ new_name,
+ new_type_str=None,
+ new_not_null=None,
+ new_default_str=None,
+ new_comment=None,
+ ):
self.table().aboutToChange.emit()
if self.name == new_name:
new_name = None
@@ -1273,21 +1533,50 @@ def update(self, new_name, new_type_str=None, new_not_null=None, new_default_str
new_default_str = None
if self.comment == new_comment:
new_comment = None
- ret = self.table().database().connector.updateTableColumn((self.table().schemaName(), self.table().name),
- self.name, new_name, new_type_str,
- new_not_null, new_default_str, new_comment)
+ ret = (
+ self.table()
+ .database()
+ .connector.updateTableColumn(
+ (self.table().schemaName(), self.table().name),
+ self.name,
+ new_name,
+ new_type_str,
+ new_not_null,
+ new_default_str,
+ new_comment,
+ )
+ )
if ret is not False:
self.table().refreshFields()
return ret
class TableConstraint(TableSubItemObject):
- """ class that represents a constraint of a table (relation) """
-
- TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique, TypeExclusion, TypeUnknown = list(range(6))
- types = {"c": TypeCheck, "f": TypeForeignKey, "p": TypePrimaryKey, "u": TypeUnique, "x": TypeExclusion}
-
- onAction = {"a": "NO ACTION", "r": "RESTRICT", "c": "CASCADE", "n": "SET NULL", "d": "SET DEFAULT"}
+ """class that represents a constraint of a table (relation)"""
+
+ (
+ TypeCheck,
+ TypeForeignKey,
+ TypePrimaryKey,
+ TypeUnique,
+ TypeExclusion,
+ TypeUnknown,
+ ) = list(range(6))
+ types = {
+ "c": TypeCheck,
+ "f": TypeForeignKey,
+ "p": TypePrimaryKey,
+ "u": TypeUnique,
+ "x": TypeExclusion,
+ }
+
+ onAction = {
+ "a": "NO ACTION",
+ "r": "RESTRICT",
+ "c": "CASCADE",
+ "n": "SET NULL",
+ "d": "SET DEFAULT",
+ }
matchTypes = {"u": "UNSPECIFIED", "f": "FULL", "p": "PARTIAL", "s": "SIMPLE"}
def __init__(self, table):
@@ -1305,11 +1594,11 @@ def type2String(self):
return QApplication.translate("DBManagerPlugin", "Unique")
if self.type == TableConstraint.TypeExclusion:
return QApplication.translate("DBManagerPlugin", "Exclusion")
- return QApplication.translate("DBManagerPlugin", 'Unknown')
+ return QApplication.translate("DBManagerPlugin", "Unknown")
def fields(self):
def fieldFromNum(num, fields):
- """ return field specified by its number or None if doesn't exist """
+ """return field specified by its number or None if doesn't exist"""
for fld in fields:
if fld.num == num:
return fld
@@ -1333,7 +1622,7 @@ def __init__(self, table):
def fields(self):
def fieldFromNum(num, fields):
- """ return field specified by its number or None if doesn't exist """
+ """return field specified by its number or None if doesn't exist"""
for fld in fields:
if fld.num == num:
return fld
@@ -1350,23 +1639,23 @@ def delete(self):
class TableTrigger(TableSubItemObject):
- """ class that represents a trigger """
+ """class that represents a trigger"""
# Bits within tgtype (pg_trigger.h)
- TypeRow = (1 << 0) # row or statement
- TypeBefore = (1 << 1) # before or after
+ TypeRow = 1 << 0 # row or statement
+ TypeBefore = 1 << 1 # before or after
# events: one or more
- TypeInsert = (1 << 2)
- TypeDelete = (1 << 3)
- TypeUpdate = (1 << 4)
- TypeTruncate = (1 << 5)
+ TypeInsert = 1 << 2
+ TypeDelete = 1 << 3
+ TypeUpdate = 1 << 4
+ TypeTruncate = 1 << 5
def __init__(self, table):
TableSubItemObject.__init__(self, table)
self.name = self.function = None
def type2String(self):
- trig_type = ''
+ trig_type = ""
trig_type += "Before " if self.type & TableTrigger.TypeBefore else "After "
if self.type & TableTrigger.TypeInsert:
trig_type += "INSERT "
diff --git a/python/plugins/db_manager/db_plugins/postgis/connector.py b/python/plugins/db_manager/db_plugins/postgis/connector.py
index afe4ae3fc3f6..af63f10dff09 100644
--- a/python/plugins/db_manager/db_plugins/postgis/connector.py
+++ b/python/plugins/db_manager/db_plugins/postgis/connector.py
@@ -52,7 +52,7 @@ def classFactory():
return PostGisDBConnector
-class CursorAdapter():
+class CursorAdapter:
def _debug(self, msg):
pass
@@ -66,7 +66,7 @@ def __init__(self, connection, sql=None, feedback=None):
self.cursor = 0
self.feedback = feedback
self.closed = False
- if (self.sql is not None):
+ if self.sql is not None:
self._execute()
def _toStrResultSet(self, res):
@@ -75,11 +75,15 @@ def _toStrResultSet(self, res):
newrec = []
for col in rec:
if type(col) == type(QVariant(None)): # noqa
- if (str(col) == 'NULL'):
+ if str(col) == "NULL":
col = None
else:
col = str(col) # force to string
- if isinstance(col, QDateTime) or isinstance(col, QDate) or isinstance(col, QTime):
+ if (
+ isinstance(col, QDateTime)
+ or isinstance(col, QDate)
+ or isinstance(col, QTime)
+ ):
col = col.toString(Qt.DateFormat.ISODate)
newrec.append(col)
newres.append(newrec)
@@ -88,9 +92,9 @@ def _toStrResultSet(self, res):
def _execute(self, sql=None):
if (sql is None or self.sql == sql) and self.result is not None:
return
- if (sql is not None):
+ if sql is not None:
self.sql = sql
- if (self.sql is None):
+ if self.sql is None:
return
self._debug("execute called with sql " + self.sql)
try:
@@ -98,15 +102,17 @@ def _execute(self, sql=None):
self._description = [] # reset description
self.result = self._toStrResultSet(result.rows())
for c in result.columns():
- self._description.append([
- c, # name
- '', # type_code
- -1, # display_size
- -1, # internal_size
- -1, # precision
- None, # scale
- True # null_ok
- ])
+ self._description.append(
+ [
+ c, # name
+ "", # type_code
+ -1, # display_size
+ -1, # internal_size
+ -1, # precision
+ None, # scale
+ True, # null_ok
+ ]
+ )
except QgsProviderConnectionException as e:
self._description = None
@@ -122,48 +128,62 @@ def description(self):
self._description = []
- if re.match('^SHOW', self.sql.strip().upper()):
+ if re.match("^SHOW", self.sql.strip().upper()):
try:
count = len(self.connection.executeSql(self.sql)[0])
except QgsProviderConnectionException:
count = 1
for i in range(count):
- self._description.append([
- '', # name
- '', # type_code
- -1, # display_size
- -1, # internal_size
- -1, # precision
- None, # scale
- True # null_ok
- ])
+ self._description.append(
+ [
+ "", # name
+ "", # type_code
+ -1, # display_size
+ -1, # internal_size
+ -1, # precision
+ None, # scale
+ True, # null_ok
+ ]
+ )
else:
uri = QgsDataSourceUri(self.connection.uri())
# TODO: make this part provider-agnostic
- sql = self.sql if self.sql.upper().find(' LIMIT ') >= 0 else self.sql + ' LIMIT 1 '
- uri.setTable('(SELECT row_number() OVER () AS __rid__, * FROM (' + sql + ') as foo)')
- uri.setKeyColumn('__rid__')
- uri.setParam('checkPrimaryKeyUnicity', '0')
+ sql = (
+ self.sql
+ if self.sql.upper().find(" LIMIT ") >= 0
+ else self.sql + " LIMIT 1 "
+ )
+ uri.setTable(
+ "(SELECT row_number() OVER () AS __rid__, * FROM ("
+ + sql
+ + ") as foo)"
+ )
+ uri.setKeyColumn("__rid__")
+ uri.setParam("checkPrimaryKeyUnicity", "0")
# TODO: fetch provider name from connection (QgsAbstractConnectionProvider)
# TODO: re-use the VectorLayer for fetching rows in batch mode
- vl = QgsVectorLayer(uri.uri(False), 'dbmanager_cursor', 'postgres')
+ vl = QgsVectorLayer(uri.uri(False), "dbmanager_cursor", "postgres")
fields = vl.fields()
for i in range(1, len(fields)): # skip first field (__rid__)
f = fields[i]
- self._description.append([
- f.name(), # name
- f.type(), # type_code
- f.length(), # display_size
- f.length(), # internal_size
- f.precision(), # precision
- None, # scale
- True # null_ok
- ])
-
- self._debug("get_description returned " + str(len(self._description)) + " cols")
+ self._description.append(
+ [
+ f.name(), # name
+ f.type(), # type_code
+ f.length(), # display_size
+ f.length(), # internal_size
+ f.precision(), # precision
+ None, # scale
+ True, # null_ok
+ ]
+ )
+
+ self._debug(
+ "get_description returned " + str(len(self._description)) + " cols"
+ )
return self._description
@@ -178,33 +198,49 @@ def fetchone(self):
def fetchmany(self, size):
self._execute()
if self.result is None:
- self._debug("fetchmany: none result after _execute (self.sql is " + str(self.sql) + ", returning []")
+ self._debug(
+ "fetchmany: none result after _execute (self.sql is "
+ + str(self.sql)
+ + ", returning []"
+ )
return []
leftover = len(self.result) - self.cursor
- self._debug("fetchmany: cursor: " + str(self.cursor) + " leftover: " + str(leftover) + " requested: " + str(size))
+ self._debug(
+ "fetchmany: cursor: "
+ + str(self.cursor)
+ + " leftover: "
+ + str(leftover)
+ + " requested: "
+ + str(size)
+ )
if leftover < 1:
return []
if size > leftover:
size = leftover
stop = self.cursor + size
- res = self.result[self.cursor:stop]
+ res = self.result[self.cursor : stop]
self.cursor = stop
- self._debug("fetchmany: new cursor: " + str(self.cursor) + " reslen: " + str(len(self.result)))
+ self._debug(
+ "fetchmany: new cursor: "
+ + str(self.cursor)
+ + " reslen: "
+ + str(len(self.result))
+ )
return res
def fetchall(self):
self._execute()
- res = self.result[self.cursor:]
+ res = self.result[self.cursor :]
self.cursor = len(self.result)
return res
- def scroll(self, pos, mode='relative'):
+ def scroll(self, pos, mode="relative"):
self._execute()
if pos < 0:
self._debug("scroll pos is negative: " + str(pos))
- if mode == 'relative':
+ if mode == "relative":
self.cursor = self.cursor + pos
- elif mode == 'absolute':
+ elif mode == "absolute":
self.cursor = pos
def close(self):
@@ -224,13 +260,13 @@ def __init__(self, uri, connection):
"""
DBConnector.__init__(self, uri)
- username = uri.username() or os.environ.get('PGUSER')
+ username = uri.username() or os.environ.get("PGUSER")
# Do not get db and user names from the env if service is used
if not uri.service():
if username is None:
- username = os.environ.get('USER')
- self.dbname = uri.database() or os.environ.get('PGDATABASE') or username
+ username = os.environ.get("USER")
+ self.dbname = uri.database() or os.environ.get("PGDATABASE") or username
uri.setDatabase(self.dbname)
# self.connName = connName
@@ -269,9 +305,13 @@ def removeCert(certFile):
# On linux and Mac if file is set with QFile::>ReadUser
# does not create problem removing certs
if not file.setPermissions(QFile.Permission.WriteOwner):
- raise Exception('Cannot change permissions on {}: error code: {}'.format(file.fileName(), file.error()))
+ raise Exception(
+ f"Cannot change permissions on {file.fileName()}: error code: {file.error()}"
+ )
if not file.remove():
- raise Exception('Cannot remove {}: error code: {}'.format(file.fileName(), file.error()))
+ raise Exception(
+ f"Cannot remove {file.fileName()}: error code: {file.error()}"
+ )
sslCertFile = expandedUri.param("sslcert")
if sslCertFile:
@@ -286,48 +326,59 @@ def removeCert(certFile):
removeCert(sslCAFile)
def _checkSpatial(self):
- """ check whether postgis_version is present in catalog """
- c = self._execute(None, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'")
+ """check whether postgis_version is present in catalog"""
+ c = self._execute(
+ None, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'"
+ )
self.has_spatial = self._fetchone(c)[0] > 0
self._close_cursor(c)
return self.has_spatial
def _checkRaster(self):
- """ check whether postgis_version is present in catalog """
- c = self._execute(None, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_raster_lib_version'")
+ """check whether postgis_version is present in catalog"""
+ c = self._execute(
+ None,
+ "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_raster_lib_version'",
+ )
self.has_raster = self._fetchone(c)[0] > 0
self._close_cursor(c)
return self.has_raster
def _checkGeometryColumnsTable(self):
- c = self._execute(None,
- "SELECT relkind = 'v' OR relkind = 'm' FROM pg_class WHERE relname = 'geometry_columns' AND relkind IN ('v', 'r', 'm', 'p')")
+ c = self._execute(
+ None,
+ "SELECT relkind = 'v' OR relkind = 'm' FROM pg_class WHERE relname = 'geometry_columns' AND relkind IN ('v', 'r', 'm', 'p')",
+ )
res = self._fetchone(c)
self._close_cursor(c)
- self.has_geometry_columns = (res is not None and len(res) != 0)
+ self.has_geometry_columns = res is not None and len(res) != 0
if not self.has_geometry_columns:
self.has_geometry_columns_access = self.is_geometry_columns_view = False
else:
self.is_geometry_columns_view = res[0]
# find out whether has privileges to access geometry_columns table
- priv = self.getTablePrivileges('geometry_columns')
+ priv = self.getTablePrivileges("geometry_columns")
self.has_geometry_columns_access = priv[0]
return self.has_geometry_columns
def _checkRasterColumnsTable(self):
- c = self._execute(None,
- "SELECT relkind = 'v' OR relkind = 'm' FROM pg_class WHERE relname = 'raster_columns' AND relkind IN ('v', 'r', 'm', 'p')")
+ c = self._execute(
+ None,
+ "SELECT relkind = 'v' OR relkind = 'm' FROM pg_class WHERE relname = 'raster_columns' AND relkind IN ('v', 'r', 'm', 'p')",
+ )
res = self._fetchone(c)
self._close_cursor(c)
- self.has_raster_columns = (res is not None and len(res) != 0)
+ self.has_raster_columns = res is not None and len(res) != 0
if not self.has_raster_columns:
self.has_raster_columns_access = self.is_raster_columns_view = False
else:
self.is_raster_columns_view = res[0]
# find out whether has privileges to access geometry_columns table
- self.has_raster_columns_access = self.getTablePrivileges('raster_columns')[0]
+ self.has_raster_columns_access = self.getTablePrivileges("raster_columns")[
+ 0
+ ]
return self.has_raster_columns
def cancel(self):
@@ -350,19 +401,21 @@ def getPsqlVersion(self):
raise DbError(f"Unknown PostgreSQL version: {self.getInfo()[0]}")
def getSpatialInfo(self):
- """ returns tuple about PostGIS support:
- - lib version
- - geos version
- - proj version
- - installed scripts version
- - released scripts version
+ """returns tuple about PostGIS support:
+ - lib version
+ - geos version
+ - proj version
+ - installed scripts version
+ - released scripts version
"""
if not self.has_spatial:
return
try:
- c = self._execute(None,
- "SELECT postgis_lib_version(), postgis_geos_version(), postgis_proj_version(), postgis_scripts_installed(), postgis_scripts_released()")
+ c = self._execute(
+ None,
+ "SELECT postgis_lib_version(), postgis_geos_version(), postgis_proj_version(), postgis_scripts_installed(), postgis_scripts_released()",
+ )
except DbError:
return
res = self._fetchone(c)
@@ -386,16 +439,26 @@ def hasCreateSpatialViewSupport(self):
def fieldTypes(self):
return [
- "integer", "bigint", "smallint", # integers
- "serial", "bigserial", # auto-incrementing ints
- "real", "double precision", "numeric", # floats
- "varchar", "varchar(255)", "char(20)", "text", # strings
- "date", "time", "timestamp", # date/time
- "boolean" # bool
+ "integer",
+ "bigint",
+ "smallint", # integers
+ "serial",
+ "bigserial", # auto-incrementing ints
+ "real",
+ "double precision",
+ "numeric", # floats
+ "varchar",
+ "varchar(255)",
+ "char(20)",
+ "text", # strings
+ "date",
+ "time",
+ "timestamp", # date/time
+ "boolean", # bool
]
def getDatabasePrivileges(self):
- """ db privileges: (can create schemas, can create temp. tables) """
+ """db privileges: (can create schemas, can create temp. tables)"""
sql = "SELECT has_database_privilege(current_database(), 'CREATE'), has_database_privilege(current_database(), 'TEMP')"
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -403,16 +466,18 @@ def getDatabasePrivileges(self):
return res
def getSchemaPrivileges(self, schema):
- """ schema privileges: (can create new objects, can access objects in schema) """
- schema = 'current_schema()' if schema is None else self.quoteString(schema)
- sql = "SELECT has_schema_privilege(%(s)s, 'CREATE'), has_schema_privilege(%(s)s, 'USAGE')" % {'s': schema}
+ """schema privileges: (can create new objects, can access objects in schema)"""
+ schema = "current_schema()" if schema is None else self.quoteString(schema)
+ sql = "SELECT has_schema_privilege({s}, 'CREATE'), has_schema_privilege({s}, 'USAGE')".format(
+ s=schema
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
self._close_cursor(c)
return res
def getTablePrivileges(self, table):
- """ table privileges: (select, insert, update, delete) """
+ """table privileges: (select, insert, update, delete)"""
schema, tablename = self.getSchemaTableName(table)
schema_priv = self.getSchemaPrivileges(schema)
@@ -420,16 +485,17 @@ def getTablePrivileges(self, table):
return
t = self.quoteId(table)
- sql = """SELECT has_table_privilege(%(t)s, 'SELECT'), has_table_privilege(%(t)s, 'INSERT'),
- has_table_privilege(%(t)s, 'UPDATE'), has_table_privilege(%(t)s, 'DELETE')""" % {
- 't': self.quoteString(t)}
+ sql = """SELECT has_table_privilege({t}, 'SELECT'), has_table_privilege({t}, 'INSERT'),
+ has_table_privilege({t}, 'UPDATE'), has_table_privilege({t}, 'DELETE')""".format(
+ t=self.quoteString(t)
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
self._close_cursor(c)
return res
def getSchemas(self):
- """ get list of schemas in tuples: (oid, name, owner, perms) """
+ """get list of schemas in tuples: (oid, name, owner, perms)"""
sql = "SELECT oid, nspname, pg_get_userbyid(nspowner), nspacl, pg_catalog.obj_description(oid) FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema' ORDER BY nspname"
c = self._execute(None, sql)
@@ -438,17 +504,26 @@ def getSchemas(self):
return res
def getTables(self, schema=None, add_sys_tables=False):
- """ get list of tables """
+ """get list of tables"""
tablenames = []
items = []
- sys_tables = ["spatial_ref_sys", "geography_columns", "geometry_columns",
- "raster_columns", "raster_overviews"]
+ sys_tables = [
+ "spatial_ref_sys",
+ "geography_columns",
+ "geometry_columns",
+ "raster_columns",
+ "raster_overviews",
+ ]
try:
vectors = self.getVectorTables(schema)
for tbl in vectors:
- if not add_sys_tables and tbl[1] in sys_tables and tbl[2] in ['', 'public']:
+ if (
+ not add_sys_tables
+ and tbl[1] in sys_tables
+ and tbl[2] in ["", "public"]
+ ):
continue
tablenames.append((tbl[2], tbl[1]))
items.append(tbl)
@@ -458,30 +533,45 @@ def getTables(self, schema=None, add_sys_tables=False):
try:
rasters = self.getRasterTables(schema)
for tbl in rasters:
- if not add_sys_tables and tbl[1] in sys_tables and tbl[2] in ['', 'public']:
+ if (
+ not add_sys_tables
+ and tbl[1] in sys_tables
+ and tbl[2] in ["", "public"]
+ ):
continue
tablenames.append((tbl[2], tbl[1]))
items.append(tbl)
except DbError:
pass
- sys_tables = ["spatial_ref_sys", "geography_columns", "geometry_columns",
- "raster_columns", "raster_overviews"]
+ sys_tables = [
+ "spatial_ref_sys",
+ "geography_columns",
+ "geometry_columns",
+ "raster_columns",
+ "raster_overviews",
+ ]
if schema:
schema_where = " AND nspname = %s " % self.quoteString(schema)
else:
- schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ schema_where = (
+ " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ )
# get all tables and views
- sql = """SELECT
+ sql = (
+ """SELECT
cla.relname, nsp.nspname, cla.relkind,
pg_get_userbyid(relowner), reltuples, relpages,
pg_catalog.obj_description(cla.oid)
FROM pg_class AS cla
JOIN pg_namespace AS nsp ON nsp.oid = cla.relnamespace
- WHERE cla.relkind IN ('v', 'r', 'm', 'p') """ + schema_where + """
+ WHERE cla.relkind IN ('v', 'r', 'm', 'p') """
+ + schema_where
+ + """
ORDER BY nsp.nspname, cla.relname"""
+ )
c = self._execute(None, sql)
for tbl in self._fetchall(c):
@@ -494,19 +584,19 @@ def getTables(self, schema=None, add_sys_tables=False):
return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
def getVectorTables(self, schema=None):
- """ get list of table with a geometry column
- it returns:
- name (table name)
- namespace (schema)
- type = 'view' (is a view?)
- owner
- tuples
- pages
- geometry_column:
- f_geometry_column (or pg_attribute.attname, the geometry column name)
- type (or pg_attribute.atttypid::regtype, the geometry column type name)
- coord_dimension
- srid
+ """get list of table with a geometry column
+ it returns:
+ name (table name)
+ namespace (schema)
+ type = 'view' (is a view?)
+ owner
+ tuples
+ pages
+ geometry_column:
+ f_geometry_column (or pg_attribute.attname, the geometry column name)
+ type (or pg_attribute.atttypid::regtype, the geometry column type name)
+ coord_dimension
+ srid
"""
if not self.has_spatial:
@@ -515,7 +605,9 @@ def getVectorTables(self, schema=None):
if schema:
schema_where = " AND nspname = %s " % self.quoteString(schema)
else:
- schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ schema_where = (
+ " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ )
geometry_column_from = ""
geometry_fields_select = """att.attname,
@@ -530,11 +622,14 @@ def getVectorTables(self, schema=None):
geo.coord_dimension, geo.srid"""
# discovery of all tables and whether they contain a geometry column
- sql = """SELECT
+ sql = (
+ """SELECT
cla.relname, nsp.nspname, cla.relkind,
pg_get_userbyid(relowner), cla.reltuples, cla.relpages,
pg_catalog.obj_description(cla.oid),
- """ + geometry_fields_select + """
+ """
+ + geometry_fields_select
+ + """
FROM pg_class AS cla
JOIN pg_namespace AS nsp ON
@@ -545,10 +640,15 @@ def getVectorTables(self, schema=None):
att.atttypid = 'geometry'::regtype OR
att.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='geometry'::regtype )
- """ + geometry_column_from + """
+ """
+ + geometry_column_from
+ + """
- WHERE cla.relkind IN ('v', 'r', 'm', 'p') """ + schema_where + """
+ WHERE cla.relkind IN ('v', 'r', 'm', 'p') """
+ + schema_where
+ + """
ORDER BY nsp.nspname, cla.relname, att.attname"""
+ )
items = []
@@ -562,20 +662,20 @@ def getVectorTables(self, schema=None):
return items
def getRasterTables(self, schema=None):
- """ get list of table with a raster column
- it returns:
- name (table name)
- namespace (schema)
- type = 'view' (is a view?)
- owner
- tuples
- pages
- raster_column:
- r_raster_column (or pg_attribute.attname, the raster column name)
- pixel type
- block size
- internal or external
- srid
+ """get list of table with a raster column
+ it returns:
+ name (table name)
+ namespace (schema)
+ type = 'view' (is a view?)
+ owner
+ tuples
+ pages
+ raster_column:
+ r_raster_column (or pg_attribute.attname, the raster column name)
+ pixel type
+ block size
+ internal or external
+ srid
"""
if not self.has_spatial:
@@ -586,7 +686,9 @@ def getRasterTables(self, schema=None):
if schema:
schema_where = " AND nspname = %s " % self.quoteString(schema)
else:
- schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ schema_where = (
+ " AND (nspname != 'information_schema' AND nspname !~ 'pg_') "
+ )
raster_column_from = ""
raster_fields_select = """att.attname, NULL, NULL, NULL, NULL, NULL"""
@@ -602,11 +704,14 @@ def getRasterTables(self, schema=None):
rast.srid"""
# discovery of all tables and whether they contain a raster column
- sql = """SELECT
+ sql = (
+ """SELECT
cla.relname, nsp.nspname, cla.relkind,
pg_get_userbyid(relowner), cla.reltuples, cla.relpages,
pg_catalog.obj_description(cla.oid),
- """ + raster_fields_select + """
+ """
+ + raster_fields_select
+ + """
FROM pg_class AS cla
JOIN pg_namespace AS nsp ON
@@ -617,10 +722,15 @@ def getRasterTables(self, schema=None):
att.atttypid = 'raster'::regtype OR
att.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='raster'::regtype )
- """ + raster_column_from + """
+ """
+ + raster_column_from
+ + """
- WHERE cla.relkind IN ('v', 'r', 'm', 'p') """ + schema_where + """
+ WHERE cla.relkind IN ('v', 'r', 'm', 'p') """
+ + schema_where
+ + """
ORDER BY nsp.nspname, cla.relname, att.attname"""
+ )
items = []
@@ -640,13 +750,15 @@ def getTableRowCount(self, table):
return res
def getTableFields(self, table):
- """ return list of columns in table """
+ """return list of columns in table"""
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ )
version_number = self.getPsqlVersion()
- ad_col_name = 'adsrc' if version_number < 12 else 'adbin'
+ ad_col_name = "adsrc" if version_number < 12 else "adbin"
sql = """SELECT a.attnum AS ordinal_position,
a.attname AS column_name,
@@ -655,7 +767,7 @@ def getTableFields(self, table):
a.atttypmod AS modifier,
a.attnotnull AS notnull,
a.atthasdef AS hasdefault,
- adef.%s AS default_value,
+ adef.{} AS default_value,
pg_catalog.format_type(a.atttypid,a.atttypmod) AS formatted_type
FROM pg_class c
JOIN pg_attribute a ON a.attrelid = c.oid
@@ -663,8 +775,10 @@ def getTableFields(self, table):
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
LEFT JOIN pg_attrdef adef ON adef.adrelid = a.attrelid AND adef.adnum = a.attnum
WHERE
- a.attnum > 0 AND c.relname=%s %s
- ORDER BY a.attnum""" % (ad_col_name, self.quoteString(tablename), schema_where)
+ a.attnum > 0 AND c.relname={} {}
+ ORDER BY a.attnum""".format(
+ ad_col_name, self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -672,17 +786,20 @@ def getTableFields(self, table):
return res
def getTableIndexes(self, table):
- """ get info about table's indexes. ignore primary key constraint index, they get listed in constraints """
+ """get info about table's indexes. ignore primary key constraint index, they get listed in constraints"""
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ )
sql = """SELECT idxcls.relname, indkey, indisunique = 't'
FROM pg_index JOIN pg_class ON pg_index.indrelid=pg_class.oid
JOIN pg_class AS idxcls ON pg_index.indexrelid=idxcls.oid
JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid
- WHERE pg_class.relname=%s %s
- AND indisprimary != 't' """ % (
- self.quoteString(tablename), schema_where)
+ WHERE pg_class.relname={} {}
+ AND indisprimary != 't' """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
self._close_cursor(c)
@@ -691,20 +808,24 @@ def getTableIndexes(self, table):
def getTableConstraints(self, table):
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ )
version_number = self.getPsqlVersion()
- con_col_name = 'consrc' if version_number < 12 else 'conbin'
+ con_col_name = "consrc" if version_number < 12 else "conbin"
# In the query below, we exclude rows where pg_constraint.contype whose values are equal to 't'
# because 't' describes a CONSTRAINT TRIGGER, which is not really a constraint in the traditional
# sense, but a special type of trigger, and an extension to the SQL standard.
- sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.%s,
+ sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.{},
t2.relname, c.confupdtype, c.confdeltype, c.confmatchtype, array_to_string(c.confkey, ' ') FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
JOIN pg_namespace nsp ON t.relnamespace = nsp.oid
- WHERE c.contype <> 't' AND t.relname = %s %s """ % (con_col_name, self.quoteString(tablename), schema_where)
+ WHERE c.contype <> 't' AND t.relname = {} {} """.format(
+ con_col_name, self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -714,14 +835,17 @@ def getTableConstraints(self, table):
def getTableTriggers(self, table):
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ )
sql = """SELECT tgname, proname, tgtype, tgenabled NOT IN ('f', 'D') FROM pg_trigger trig
LEFT JOIN pg_class t ON trig.tgrelid = t.oid
LEFT JOIN pg_proc p ON trig.tgfoid = p.oid
JOIN pg_namespace nsp ON t.relnamespace = nsp.oid
- WHERE t.relname = %s %s """ % (
- self.quoteString(tablename), schema_where)
+ WHERE t.relname = {} {} """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -729,27 +853,35 @@ def getTableTriggers(self, table):
return res
def enableAllTableTriggers(self, enable, table):
- """ enable or disable all triggers on table """
+ """enable or disable all triggers on table"""
self.enableTableTrigger(None, enable, table)
def enableTableTrigger(self, trigger, enable, table):
- """ enable or disable one trigger on table """
+ """enable or disable one trigger on table"""
trigger = self.quoteId(trigger) if trigger is not None else "ALL"
- sql = "ALTER TABLE %s %s TRIGGER %s" % (self.quoteId(table), "ENABLE" if enable else "DISABLE", trigger)
+ sql = "ALTER TABLE {} {} TRIGGER {}".format(
+ self.quoteId(table), "ENABLE" if enable else "DISABLE", trigger
+ )
self._execute_and_commit(sql)
def deleteTableTrigger(self, trigger, table):
- """Deletes trigger on table """
- sql = "DROP TRIGGER %s ON %s" % (self.quoteId(trigger), self.quoteId(table))
+ """Deletes trigger on table"""
+ sql = f"DROP TRIGGER {self.quoteId(trigger)} ON {self.quoteId(table)}"
self._execute_and_commit(sql)
def getTableRules(self, table):
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND schemaname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND schemaname=%s " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
sql = """SELECT rulename, definition FROM pg_rules
- WHERE tablename=%s %s """ % (self.quoteString(tablename), schema_where)
+ WHERE tablename={} {} """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchall(c)
@@ -757,14 +889,19 @@ def getTableRules(self, table):
return res
def deleteTableRule(self, rule, table):
- """Deletes rule on table """
- sql = "DROP RULE %s ON %s" % (self.quoteId(rule), self.quoteId(table))
+ """Deletes rule on table"""
+ sql = f"DROP RULE {self.quoteId(rule)} ON {self.quoteId(table)}"
self._execute_and_commit(sql)
def getTableExtent(self, table, geom):
- """ find out table extent """
- subquery = "SELECT st_extent(%s) AS extent FROM %s" % (self.quoteId(geom), self.quoteId(table))
- sql = "SELECT st_xmin(extent), st_ymin(extent), st_xmax(extent), st_ymax(extent) FROM (%s) AS subquery" % subquery
+ """find out table extent"""
+ subquery = "SELECT st_extent({}) AS extent FROM {}".format(
+ self.quoteId(geom), self.quoteId(table)
+ )
+ sql = (
+ "SELECT st_xmin(extent), st_ymin(extent), st_xmax(extent), st_ymax(extent) FROM (%s) AS subquery"
+ % subquery
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -772,14 +909,14 @@ def getTableExtent(self, table, geom):
return res
def getTableEstimatedExtent(self, table, geom):
- """ find out estimated extent (from the statistics) """
+ """find out estimated extent (from the statistics)"""
if self.isRasterTable(table):
return
schema, tablename = self.getSchemaTableName(table)
schema_part = "%s," % self.quoteString(schema) if schema is not None else ""
- pgis_versions = self.getSpatialInfo()[0].split('.')
+ pgis_versions = self.getSpatialInfo()[0].split(".")
pgis_major_version = int(pgis_versions[0])
pgis_minor_version = int(pgis_versions[1])
pgis_old = False
@@ -787,10 +924,16 @@ def getTableEstimatedExtent(self, table, geom):
pgis_old = True
elif pgis_major_version == 2 and pgis_minor_version < 1:
pgis_old = True
- subquery = "SELECT %s(%s%s,%s) AS extent" % (
- 'st_estimated_extent' if pgis_old else 'st_estimatedextent',
- schema_part, self.quoteString(tablename), self.quoteString(geom))
- sql = """SELECT st_xmin(extent), st_ymin(extent), st_xmax(extent), st_ymax(extent) FROM (%s) AS subquery """ % subquery
+ subquery = "SELECT {}({}{},{}) AS extent".format(
+ "st_estimated_extent" if pgis_old else "st_estimatedextent",
+ schema_part,
+ self.quoteString(tablename),
+ self.quoteString(geom),
+ )
+ sql = (
+ """SELECT st_xmin(extent), st_ymin(extent), st_xmax(extent), st_ymax(extent) FROM (%s) AS subquery """
+ % subquery
+ )
try:
c = self._execute(None, sql)
@@ -801,15 +944,18 @@ def getTableEstimatedExtent(self, table, geom):
return res
def getViewDefinition(self, view):
- """ returns definition of the view """
+ """returns definition of the view"""
schema, tablename = self.getSchemaTableName(view)
- schema_where = " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " AND nspname=%s " % self.quoteString(schema) if schema is not None else ""
+ )
sql = """SELECT pg_get_viewdef(c.oid) FROM pg_class c
JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
- WHERE relname=%s %s AND (relkind='v' OR relkind='m') """ % (
- self.quoteString(tablename), schema_where)
+ WHERE relname={} {} AND (relkind='v' OR relkind='m') """.format(
+ self.quoteString(tablename), schema_where
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -821,7 +967,9 @@ def getCrs(self, srid):
return QgsCoordinateReferenceSystem()
try:
- c = self._execute(None, "SELECT proj4text FROM spatial_ref_sys WHERE srid = '%d'" % srid)
+ c = self._execute(
+ None, "SELECT proj4text FROM spatial_ref_sys WHERE srid = '%d'" % srid
+ )
except DbError:
return QgsCoordinateReferenceSystem()
res = self._fetchone(c)
@@ -838,7 +986,9 @@ def getSpatialRefInfo(self, srid):
return
try:
- c = self._execute(None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid)
+ c = self._execute(
+ None, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid
+ )
except DbError:
return
sr = self._fetchone(c)
@@ -857,8 +1007,9 @@ def getSpatialRefInfo(self, srid):
def isVectorTable(self, table):
if self.has_geometry_columns and self.has_geometry_columns_access:
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT count(*) FROM geometry_columns WHERE f_table_schema = %s AND f_table_name = %s" % (
- self.quoteString(schema), self.quoteString(tablename))
+ sql = "SELECT count(*) FROM geometry_columns WHERE f_table_schema = {} AND f_table_name = {}".format(
+ self.quoteString(schema), self.quoteString(tablename)
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -870,8 +1021,9 @@ def isVectorTable(self, table):
def isRasterTable(self, table):
if self.has_raster_columns and self.has_raster_columns_access:
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT count(*) FROM raster_columns WHERE r_table_schema = %s AND r_table_name = %s" % (
- self.quoteString(schema), self.quoteString(tablename))
+ sql = "SELECT count(*) FROM raster_columns WHERE r_table_schema = {} AND r_table_name = {}".format(
+ self.quoteString(schema), self.quoteString(tablename)
+ )
c = self._execute(None, sql)
res = self._fetchone(c)
@@ -882,8 +1034,8 @@ def isRasterTable(self, table):
def createTable(self, table, field_defs, pkey):
"""Creates ordinary table
- 'fields' is array containing field definitions
- 'pkey' is the primary key name
+ 'fields' is array containing field definitions
+ 'pkey' is the primary key name
"""
if len(field_defs) == 0:
return False
@@ -898,11 +1050,13 @@ def createTable(self, table, field_defs, pkey):
return True
def deleteTable(self, table):
- """Deletes table and its reference in either geometry_columns or raster_columns """
+ """Deletes table and its reference in either geometry_columns or raster_columns"""
schema, tablename = self.getSchemaTableName(table)
schema_part = "%s, " % self.quoteString(schema) if schema is not None else ""
if self.isVectorTable(table):
- sql = "SELECT DropGeometryTable(%s%s)" % (schema_part, self.quoteString(tablename))
+ sql = "SELECT DropGeometryTable({}{})".format(
+ schema_part, self.quoteString(tablename)
+ )
elif self.isRasterTable(table):
# Fix #8521: delete raster table and references from raster_columns table
sql = "DROP TABLE %s" % self.quoteId(table)
@@ -911,24 +1065,31 @@ def deleteTable(self, table):
self._execute_and_commit(sql)
def emptyTable(self, table):
- """Deletes all rows from table """
+ """Deletes all rows from table"""
sql = "TRUNCATE %s" % self.quoteId(table)
self._execute_and_commit(sql)
def renameTable(self, table, new_table):
- """Renames a table in database """
+ """Renames a table in database"""
schema, tablename = self.getSchemaTableName(table)
if new_table == tablename:
return
- sql = "ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(new_table))
+ sql = "ALTER TABLE {} RENAME TO {}".format(
+ self.quoteId(table), self.quoteId(new_table)
+ )
self._executeSql(sql)
# update geometry_columns if PostGIS is enabled
if self.has_geometry_columns and not self.is_geometry_columns_view:
- schema_where = " AND f_table_schema=%s " % self.quoteString(schema) if schema is not None else ""
- sql = "UPDATE geometry_columns SET f_table_name=%s WHERE f_table_name=%s %s" % (
- self.quoteString(new_table), self.quoteString(tablename), schema_where)
+ schema_where = (
+ " AND f_table_schema=%s " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
+ sql = "UPDATE geometry_columns SET f_table_name={} WHERE f_table_name={} {}".format(
+ self.quoteString(new_table), self.quoteString(tablename), schema_where
+ )
self._executeSql(sql)
def renameSchema(self, schema, new_schema):
@@ -940,16 +1101,23 @@ def renameSchema(self, schema, new_schema):
def commentTable(self, schema, tablename, comment=None):
if comment is None:
- self._execute(None, 'COMMENT ON TABLE "{}"."{}" IS NULL;'.format(schema, tablename))
+ self._execute(None, f'COMMENT ON TABLE "{schema}"."{tablename}" IS NULL;')
else:
- self._execute(None, 'COMMENT ON TABLE "{}"."{}" IS $escape${}$escape$;'.format(schema, tablename, comment))
+ self._execute(
+ None,
+ f'COMMENT ON TABLE "{schema}"."{tablename}" IS $escape${comment}$escape$;',
+ )
def getComment(self, tablename, field):
"""Returns the comment for a field"""
# SQL Query checking if a comment exists for the field
- sql_cpt = "Select count(*) from pg_description pd, pg_class pc, pg_attribute pa where relname = '%s' and attname = '%s' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum" % (tablename, field)
+ sql_cpt = "Select count(*) from pg_description pd, pg_class pc, pg_attribute pa where relname = '{}' and attname = '{}' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum".format(
+ tablename, field
+ )
# SQL Query that return the comment of the field
- sql = "Select pd.description from pg_description pd, pg_class pc, pg_attribute pa where relname = '%s' and attname = '%s' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum" % (tablename, field)
+ sql = "Select pd.description from pg_description pd, pg_class pc, pg_attribute pa where relname = '{}' and attname = '{}' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum".format(
+ tablename, field
+ )
c = self._execute(None, sql_cpt) # Execute Check query
res = self._fetchone(c)[0] # Store result
if res == 1:
@@ -959,7 +1127,7 @@ def getComment(self, tablename, field):
self._close_cursor(c) # Close cursor
return res # Return comment
else:
- return ''
+ return ""
def moveTableToSchema(self, table, new_schema):
schema, tablename = self.getSchemaTableName(table)
@@ -968,15 +1136,22 @@ def moveTableToSchema(self, table, new_schema):
c = self._get_cursor()
- sql = "ALTER TABLE %s SET SCHEMA %s" % (self.quoteId(table), self.quoteId(new_schema))
+ sql = "ALTER TABLE {} SET SCHEMA {}".format(
+ self.quoteId(table), self.quoteId(new_schema)
+ )
self._execute(c, sql)
# update geometry_columns if PostGIS is enabled
if self.has_geometry_columns and not self.is_geometry_columns_view:
schema, tablename = self.getSchemaTableName(table)
- schema_where = " AND f_table_schema=%s " % self.quoteString(schema) if schema is not None else ""
- sql = "UPDATE geometry_columns SET f_table_schema=%s WHERE f_table_name=%s %s" % (
- self.quoteString(new_schema), self.quoteString(tablename), schema_where)
+ schema_where = (
+ " AND f_table_schema=%s " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
+ sql = "UPDATE geometry_columns SET f_table_schema={} WHERE f_table_name={} {}".format(
+ self.quoteString(new_schema), self.quoteString(tablename), schema_where
+ )
self._execute(c, sql)
self._commit()
@@ -993,100 +1168,145 @@ def moveTable(self, table, new_table, new_schema=None):
c = self._get_cursor()
t = "__new_table__"
- sql = "ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(t))
+ sql = "ALTER TABLE {} RENAME TO {}".format(
+ self.quoteId(table), self.quoteId(t)
+ )
self._execute(c, sql)
- sql = "ALTER TABLE %s SET SCHEMA %s" % (self.quoteId((schema, t)), self.quoteId(new_schema))
+ sql = "ALTER TABLE {} SET SCHEMA {}".format(
+ self.quoteId((schema, t)), self.quoteId(new_schema)
+ )
self._execute(c, sql)
- sql = "ALTER TABLE %s RENAME TO %s" % (self.quoteId((new_schema, t)), self.quoteId(table))
+ sql = "ALTER TABLE {} RENAME TO {}".format(
+ self.quoteId((new_schema, t)), self.quoteId(table)
+ )
self._execute(c, sql)
# update geometry_columns if PostGIS is enabled
if self.has_geometry_columns and not self.is_geometry_columns_view:
schema, tablename = self.getSchemaTableName(table)
- schema_where = " f_table_schema=%s AND " % self.quoteString(schema) if schema is not None else ""
- schema_part = " f_table_schema=%s, " % self.quoteString(new_schema) if schema is not None else ""
- sql = "UPDATE geometry_columns SET %s f_table_name=%s WHERE %s f_table_name=%s" % (
- schema_part, self.quoteString(new_table), schema_where, self.quoteString(tablename))
+ schema_where = (
+ " f_table_schema=%s AND " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
+ schema_part = (
+ " f_table_schema=%s, " % self.quoteString(new_schema)
+ if schema is not None
+ else ""
+ )
+ sql = "UPDATE geometry_columns SET {} f_table_name={} WHERE {} f_table_name={}".format(
+ schema_part,
+ self.quoteString(new_table),
+ schema_where,
+ self.quoteString(tablename),
+ )
self._execute(c, sql)
self._commit()
def createView(self, view, query):
- view_name_parts = view.split('.')
+ view_name_parts = view.split(".")
if len(view_name_parts) > 2:
# Raise an error when more than one period is used.
- raise DbError("Invalid view name: Please use the format 'schema.viewname', or enter only the viewname for the public schema.")
+ raise DbError(
+ "Invalid view name: Please use the format 'schema.viewname', or enter only the viewname for the public schema."
+ )
elif len(view_name_parts) == 2: # To allow view creation into specified schema
schema, view_name = view_name_parts
- sql = "CREATE VIEW %s AS %s" % (self.quoteId([schema, view_name]), query)
+ sql = "CREATE VIEW {} AS {}".format(
+ self.quoteId([schema, view_name]), query
+ )
else: # No specific schema specified
- sql = "CREATE VIEW %s AS %s" % (self.quoteId(view), query)
+ sql = f"CREATE VIEW {self.quoteId(view)} AS {query}"
self._execute_and_commit(sql)
def createSpatialView(self, view, query):
self.createView(view, query)
def deleteView(self, view, isMaterialized=False):
- sql = "DROP %s VIEW %s" % ('MATERIALIZED' if isMaterialized else '', self.quoteId(view))
+ sql = "DROP {} VIEW {}".format(
+ "MATERIALIZED" if isMaterialized else "", self.quoteId(view)
+ )
self._execute_and_commit(sql)
def renameView(self, view, new_name):
- """Renames view in database """
+ """Renames view in database"""
self.renameTable(view, new_name)
def createSchema(self, schema):
- """Creates a new empty schema in database """
+ """Creates a new empty schema in database"""
sql = "CREATE SCHEMA %s" % self.quoteId(schema)
self._execute_and_commit(sql)
def deleteSchema(self, schema):
- """Drops (empty) schema from database """
+ """Drops (empty) schema from database"""
sql = "DROP SCHEMA %s" % self.quoteId(schema)
self._execute_and_commit(sql)
def renamesSchema(self, schema, new_schema):
- """Renames a schema in database """
- sql = "ALTER SCHEMA %s RENAME TO %s" % (self.quoteId(schema), self.quoteId(new_schema))
+ """Renames a schema in database"""
+ sql = "ALTER SCHEMA {} RENAME TO {}".format(
+ self.quoteId(schema), self.quoteId(new_schema)
+ )
self._execute_and_commit(sql)
def runVacuum(self):
- """Runs vacuum on the db """
+ """Runs vacuum on the db"""
self._execute_and_commit("VACUUM")
def runVacuumAnalyze(self, table):
- """Runs vacuum analyze on a table """
+ """Runs vacuum analyze on a table"""
sql = "VACUUM ANALYZE %s" % self.quoteId(table)
self._execute(None, sql)
self._commit()
def runRefreshMaterializedView(self, table):
- """Runs refresh materialized view on a table """
+ """Runs refresh materialized view on a table"""
sql = "REFRESH MATERIALIZED VIEW %s" % self.quoteId(table)
self._execute(None, sql)
self._commit()
def addTableColumn(self, table, field_def):
- """Adds a column to table """
- sql = "ALTER TABLE %s ADD %s" % (self.quoteId(table), field_def)
+ """Adds a column to table"""
+ sql = f"ALTER TABLE {self.quoteId(table)} ADD {field_def}"
self._execute_and_commit(sql)
def deleteTableColumn(self, table, column):
- """Deletes column from a table """
+ """Deletes column from a table"""
if self.isGeometryColumn(table, column):
# use PostGIS function to delete geometry column correctly
schema, tablename = self.getSchemaTableName(table)
schema_part = "%s, " % self.quoteString(schema) if schema else ""
- sql = "SELECT DropGeometryColumn(%s%s, %s)" % (
- schema_part, self.quoteString(tablename), self.quoteString(column))
+ sql = "SELECT DropGeometryColumn({}{}, {})".format(
+ schema_part, self.quoteString(tablename), self.quoteString(column)
+ )
else:
- sql = "ALTER TABLE %s DROP %s" % (self.quoteId(table), self.quoteId(column))
+ sql = "ALTER TABLE {} DROP {}".format(
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
- def updateTableColumn(self, table, column, new_name=None, data_type=None, not_null=None, default=None, comment=None, test=None):
- if new_name is None and data_type is None and not_null is None and default is None and comment is None:
+ def updateTableColumn(
+ self,
+ table,
+ column,
+ new_name=None,
+ data_type=None,
+ not_null=None,
+ default=None,
+ comment=None,
+ test=None,
+ ):
+ if (
+ new_name is None
+ and data_type is None
+ and not_null is None
+ and default is None
+ and comment is None
+ ):
return
c = self._get_cursor()
@@ -1098,7 +1318,7 @@ def updateTableColumn(self, table, column, new_name=None, data_type=None, not_nu
if not_null is not None:
col_actions.append("SET NOT NULL" if not_null else "DROP NOT NULL")
if default is not None:
- if default and default != '':
+ if default and default != "":
col_actions.append("SET DEFAULT %s" % default)
else:
col_actions.append("DROP DEFAULT")
@@ -1106,91 +1326,125 @@ def updateTableColumn(self, table, column, new_name=None, data_type=None, not_nu
sql = "ALTER TABLE %s" % self.quoteId(table)
alter_col_str = "ALTER %s" % self.quoteId(column)
for a in col_actions:
- sql += " %s %s," % (alter_col_str, a)
+ sql += f" {alter_col_str} {a},"
self._execute(c, sql[:-1])
# Renames the column
if new_name is not None and new_name != column:
- sql = "ALTER TABLE %s RENAME %s TO %s" % (
- self.quoteId(table), self.quoteId(column), self.quoteId(new_name))
+ sql = "ALTER TABLE {} RENAME {} TO {}".format(
+ self.quoteId(table), self.quoteId(column), self.quoteId(new_name)
+ )
self._execute(c, sql)
# update geometry_columns if PostGIS is enabled
if self.has_geometry_columns and not self.is_geometry_columns_view:
schema, tablename = self.getSchemaTableName(table)
- schema_where = " f_table_schema=%s AND " % self.quoteString(schema) if schema is not None else ""
- sql = "UPDATE geometry_columns SET f_geometry_column=%s WHERE %s f_table_name=%s AND f_geometry_column=%s" % (
- self.quoteString(new_name), schema_where, self.quoteString(tablename), self.quoteString(column))
+ schema_where = (
+ " f_table_schema=%s AND " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
+ sql = "UPDATE geometry_columns SET f_geometry_column={} WHERE {} f_table_name={} AND f_geometry_column={}".format(
+ self.quoteString(new_name),
+ schema_where,
+ self.quoteString(tablename),
+ self.quoteString(column),
+ )
self._execute(c, sql)
# comment the column
if comment is not None:
schema, tablename = self.getSchemaTableName(table)
- column_name = new_name if new_name is not None and new_name != column else column
- sql = "COMMENT ON COLUMN %s.%s.%s IS '%s'" % (schema, tablename, column_name, comment)
+ column_name = (
+ new_name if new_name is not None and new_name != column else column
+ )
+ sql = "COMMENT ON COLUMN {}.{}.{} IS '{}'".format(
+ schema, tablename, column_name, comment
+ )
self._execute(c, sql)
self._commit()
def renamesTableColumn(self, table, column, new_name):
- """Renames column in a table """
+ """Renames column in a table"""
return self.updateTableColumn(table, column, new_name)
def setTableColumnType(self, table, column, data_type):
- """Changes column type """
+ """Changes column type"""
return self.updateTableColumn(table, column, None, data_type)
def setTableColumnNull(self, table, column, is_null):
- """Changes whether column can contain null values """
+ """Changes whether column can contain null values"""
return self.updateTableColumn(table, column, None, None, not is_null)
def setTableColumnDefault(self, table, column, default):
"""Changes column's default value.
- If default=None or an empty string drop default value """
+ If default=None or an empty string drop default value"""
return self.updateTableColumn(table, column, None, None, None, default)
def isGeometryColumn(self, table, column):
schema, tablename = self.getSchemaTableName(table)
- schema_where = " f_table_schema=%s AND " % self.quoteString(schema) if schema is not None else ""
+ schema_where = (
+ " f_table_schema=%s AND " % self.quoteString(schema)
+ if schema is not None
+ else ""
+ )
- sql = "SELECT count(*) > 0 FROM geometry_columns WHERE %s f_table_name=%s AND f_geometry_column=%s" % (
- schema_where, self.quoteString(tablename), self.quoteString(column))
+ sql = "SELECT count(*) > 0 FROM geometry_columns WHERE {} f_table_name={} AND f_geometry_column={}".format(
+ schema_where, self.quoteString(tablename), self.quoteString(column)
+ )
c = self._execute(None, sql)
- res = self._fetchone(c)[0] == 't'
+ res = self._fetchone(c)[0] == "t"
self._close_cursor(c)
return res
- def addGeometryColumn(self, table, geom_column='geom', geom_type='POINT', srid=-1, dim=2):
+ def addGeometryColumn(
+ self, table, geom_column="geom", geom_type="POINT", srid=-1, dim=2
+ ):
schema, tablename = self.getSchemaTableName(table)
schema_part = "%s, " % self.quoteString(schema) if schema else ""
sql = "SELECT AddGeometryColumn(%s%s, %s, %d, %s, %d)" % (
- schema_part, self.quoteString(tablename), self.quoteString(geom_column), srid, self.quoteString(geom_type), dim)
+ schema_part,
+ self.quoteString(tablename),
+ self.quoteString(geom_column),
+ srid,
+ self.quoteString(geom_type),
+ dim,
+ )
self._execute_and_commit(sql)
def deleteGeometryColumn(self, table, geom_column):
return self.deleteTableColumn(table, geom_column)
def addTableUniqueConstraint(self, table, column):
- """Adds a unique constraint to a table """
- sql = "ALTER TABLE %s ADD UNIQUE (%s)" % (self.quoteId(table), self.quoteId(column))
+ """Adds a unique constraint to a table"""
+ sql = "ALTER TABLE {} ADD UNIQUE ({})".format(
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def deleteTableConstraint(self, table, constraint):
- """Deletes constraint in a table """
- sql = "ALTER TABLE %s DROP CONSTRAINT %s" % (self.quoteId(table), self.quoteId(constraint))
+ """Deletes constraint in a table"""
+ sql = "ALTER TABLE {} DROP CONSTRAINT {}".format(
+ self.quoteId(table), self.quoteId(constraint)
+ )
self._execute_and_commit(sql)
def addTablePrimaryKey(self, table, column):
- """Adds a primery key (with one column) to a table """
- sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
+ """Adds a primery key (with one column) to a table"""
+ sql = "ALTER TABLE {} ADD PRIMARY KEY ({})".format(
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def createTableIndex(self, table, name, column):
- """Creates index on one column using default options """
- sql = "CREATE INDEX %s ON %s (%s)" % (self.quoteId(name), self.quoteId(table), self.quoteId(column))
+ """Creates index on one column using default options"""
+ sql = "CREATE INDEX {} ON {} ({})".format(
+ self.quoteId(name), self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def deleteTableIndex(self, table, name):
@@ -1198,15 +1452,17 @@ def deleteTableIndex(self, table, name):
sql = "DROP INDEX %s" % self.quoteId((schema, name))
self._execute_and_commit(sql)
- def createSpatialIndex(self, table, geom_column='geom'):
+ def createSpatialIndex(self, table, geom_column="geom"):
schema, tablename = self.getSchemaTableName(table)
- idx_name = self.quoteId("sidx_%s_%s" % (tablename, geom_column))
- sql = "CREATE INDEX %s ON %s USING GIST(%s)" % (idx_name, self.quoteId(table), self.quoteId(geom_column))
+ idx_name = self.quoteId(f"sidx_{tablename}_{geom_column}")
+ sql = "CREATE INDEX {} ON {} USING GIST({})".format(
+ idx_name, self.quoteId(table), self.quoteId(geom_column)
+ )
self._execute_and_commit(sql)
- def deleteSpatialIndex(self, table, geom_column='geom'):
+ def deleteSpatialIndex(self, table, geom_column="geom"):
schema, tablename = self.getSchemaTableName(table)
- idx_name = self.quoteId("sidx_%s_%s" % (tablename, geom_column))
+ idx_name = self.quoteId(f"sidx_{tablename}_{geom_column}")
return self.deleteTableIndex(table, idx_name)
def _execute(self, cursor, sql):
@@ -1245,10 +1501,7 @@ def getSqlDictionary(self):
UNION SELECT relname FROM pg_class WHERE relkind IN ('v', 'r', 'm', 'p')
UNION SELECT attname FROM pg_attribute WHERE attnum > 0"""
c = self._execute(None, sql)
- items = [
- row[0]
- for row in self._fetchall(c)
- ]
+ items = [row[0] for row in self._fetchall(c)]
self._close_cursor(c)
sql_dict["identifier"] = items
diff --git a/python/plugins/db_manager/db_plugins/postgis/connector_test.py b/python/plugins/db_manager/db_plugins/postgis/connector_test.py
index b8be72f5a3f3..e943fe8929a5 100644
--- a/python/plugins/db_manager/db_plugins/postgis/connector_test.py
+++ b/python/plugins/db_manager/db_plugins/postgis/connector_test.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Sandro Santilli'
-__date__ = 'May 2017'
-__copyright__ = '(C) 2017, Sandro Santilli'
+__author__ = "Sandro Santilli"
+__date__ = "May 2017"
+__copyright__ = "(C) 2017, Sandro Santilli"
import os
import unittest
@@ -51,8 +51,8 @@ def _getDatabase(self, connector):
# and https://github.com/qgis/QGIS/issues/19005
def test_dbnameLessURI(self):
obj = QObject() # needs to be kept alive
- obj.connectionName = lambda: 'fake'
- obj.providerName = lambda: 'postgres'
+ obj.connectionName = lambda: "fake"
+ obj.providerName = lambda: "postgres"
c = PostGisDBConnector(QgsDataSourceUri(), obj)
self.assertIsInstance(c, PostGisDBConnector)
@@ -60,18 +60,18 @@ def test_dbnameLessURI(self):
# No username was passed, so we expect it to be taken
# from PGUSER or USER environment variables
- expected_user = os.environ.get('PGUSER') or os.environ.get('USER')
+ expected_user = os.environ.get("PGUSER") or os.environ.get("USER")
actual_user = self._getUser(c)
self.assertEqual(actual_user, expected_user)
# No database was passed, so we expect it to be taken
# from PGDATABASE or expected user
- expected_db = os.environ.get('PGDATABASE') or expected_user
+ expected_db = os.environ.get("PGDATABASE") or expected_user
actual_db = self._getDatabase(c)
self.assertEqual(actual_db, expected_db)
# TODO: add service-only test (requires a ~/.pg_service.conf file)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/python/plugins/db_manager/db_plugins/postgis/data_model.py b/python/plugins/db_manager/db_plugins/postgis/data_model.py
index be3f7b00667d..269e9713b5f6 100644
--- a/python/plugins/db_manager/db_plugins/postgis/data_model.py
+++ b/python/plugins/db_manager/db_plugins/postgis/data_model.py
@@ -20,10 +20,12 @@
from qgis.core import QgsMessageLog
from ..plugin import BaseError
-from ..data_model import (TableDataModel,
- SqlResultModel,
- SqlResultModelAsync,
- SqlResultModelTask)
+from ..data_model import (
+ TableDataModel,
+ SqlResultModel,
+ SqlResultModelAsync,
+ SqlResultModelTask,
+)
class PGTableDataModel(TableDataModel):
@@ -45,17 +47,21 @@ def _createCursor(self):
table_txt = self.db.quoteId((self.table.schemaName(), self.table.name))
self.cursor = self.db._get_cursor()
- sql = "SELECT %s FROM %s" % (fields_txt, table_txt)
+ sql = f"SELECT {fields_txt} FROM {table_txt}"
self.db._execute(self.cursor, sql)
def _sanitizeTableField(self, field):
# get fields, ignore geometry columns
if field.dataType.lower() == "geometry":
- return "CASE WHEN %(fld)s IS NULL THEN NULL ELSE GeometryType(%(fld)s) END AS %(fld)s" % {
- 'fld': self.db.quoteId(field.name)}
+ return "CASE WHEN {fld} IS NULL THEN NULL ELSE GeometryType({fld}) END AS {fld}".format(
+ fld=self.db.quoteId(field.name)
+ )
elif field.dataType.lower() == "raster":
- return "CASE WHEN %(fld)s IS NULL THEN NULL ELSE 'RASTER' END AS %(fld)s" % {
- 'fld': self.db.quoteId(field.name)}
+ return (
+ "CASE WHEN {fld} IS NULL THEN NULL ELSE 'RASTER' END AS {fld}".format(
+ fld=self.db.quoteId(field.name)
+ )
+ )
return "%s::text" % self.db.quoteId(field.name)
def _deleteCursor(self):
@@ -72,7 +78,7 @@ def fetchMoreData(self, row_start):
self._createCursor()
try:
- self.cursor.scroll(row_start, mode='absolute')
+ self.cursor.scroll(row_start, mode="absolute")
except self.db.error_types():
self._deleteCursor()
return self.fetchMoreData(row_start)
diff --git a/python/plugins/db_manager/db_plugins/postgis/info_model.py b/python/plugins/db_manager/db_plugins/postgis/info_model.py
index fa9dc3abb2e2..c5dda0b3e2fc 100644
--- a/python/plugins/db_manager/db_plugins/postgis/info_model.py
+++ b/python/plugins/db_manager/db_plugins/postgis/info_model.py
@@ -21,16 +21,31 @@
from qgis.PyQt.QtWidgets import QApplication
from ..info_model import TableInfo, VectorTableInfo, RasterTableInfo, DatabaseInfo
-from ..html_elems import HtmlSection, HtmlParagraph, HtmlTable, HtmlTableHeader, HtmlTableCol
+from ..html_elems import (
+ HtmlSection,
+ HtmlParagraph,
+ HtmlTable,
+ HtmlTableHeader,
+ HtmlTableCol,
+)
class PGDatabaseInfo(DatabaseInfo):
def connectionDetails(self):
tbl = [
- (QApplication.translate("DBManagerPlugin", "Host:"), self.db.connector.host),
- (QApplication.translate("DBManagerPlugin", "User:"), self.db.connector.user),
- (QApplication.translate("DBManagerPlugin", "Database:"), self.db.connector.dbname)
+ (
+ QApplication.translate("DBManagerPlugin", "Host:"),
+ self.db.connector.host,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "User:"),
+ self.db.connector.user,
+ ),
+ (
+ QApplication.translate("DBManagerPlugin", "Database:"),
+ self.db.connector.dbname,
+ ),
]
return HtmlTable(tbl)
@@ -53,40 +68,81 @@ def generalInfo(self):
self.table.blockSignals(False)
tbl = [
- (QApplication.translate("DBManagerPlugin", "Relation type:"),
- QApplication.translate("DBManagerPlugin", "View") if self.table._relationType == 'v' else
- QApplication.translate("DBManagerPlugin", "Materialized view") if self.table._relationType == 'm' else
- QApplication.translate("DBManagerPlugin", "Table")),
- (QApplication.translate("DBManagerPlugin", "Owner:"), self.table.owner)
+ (
+ QApplication.translate("DBManagerPlugin", "Relation type:"),
+ (
+ QApplication.translate("DBManagerPlugin", "View")
+ if self.table._relationType == "v"
+ else (
+ QApplication.translate("DBManagerPlugin", "Materialized view")
+ if self.table._relationType == "m"
+ else QApplication.translate("DBManagerPlugin", "Table")
+ )
+ ),
+ ),
+ (QApplication.translate("DBManagerPlugin", "Owner:"), self.table.owner),
]
if self.table.comment:
- tbl.append((QApplication.translate("DBManagerPlugin", "Comment:"), self.table.comment))
-
- tbl.extend([
- (QApplication.translate("DBManagerPlugin", "Pages:"), self.table.pages),
- (QApplication.translate("DBManagerPlugin", "Rows (estimation):"), self.table.estimatedRowCount)
- ])
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Comment:"),
+ self.table.comment,
+ )
+ )
+
+ tbl.extend(
+ [
+ (QApplication.translate("DBManagerPlugin", "Pages:"), self.table.pages),
+ (
+ QApplication.translate("DBManagerPlugin", "Rows (estimation):"),
+ self.table.estimatedRowCount,
+ ),
+ ]
+ )
# privileges
# has the user access to this schema?
- schema_priv = self.table.database().connector.getSchemaPrivileges(
- self.table.schemaName()) if self.table.schema() else None
+ schema_priv = (
+ self.table.database().connector.getSchemaPrivileges(self.table.schemaName())
+ if self.table.schema()
+ else None
+ )
if schema_priv is None:
pass
elif not schema_priv[1]: # no usage privileges on the schema
- tbl.append((QApplication.translate("DBManagerPlugin", "Privileges:"),
- QApplication.translate("DBManagerPlugin",
- " This user doesn't have usage privileges for this schema!")))
+ tbl.append(
+ (
+ QApplication.translate("DBManagerPlugin", "Privileges:"),
+ QApplication.translate(
+ "DBManagerPlugin",
+ " This user doesn't have usage privileges for this schema!",
+ ),
+ )
+ )
else:
- table_priv = self.table.database().connector.getTablePrivileges((self.table.schemaName(), self.table.name))
+ table_priv = self.table.database().connector.getTablePrivileges(
+ (self.table.schemaName(), self.table.name)
+ )
privileges = []
if table_priv[0]:
privileges.append("select")
if self.table.rowCount is not None and self.table.rowCount >= 0:
- tbl.append((QApplication.translate("DBManagerPlugin", "Rows (counted):"),
- self.table.rowCount if self.table.rowCount is not None else QApplication.translate(
- "DBManagerPlugin", 'Unknown (find out)')))
+ tbl.append(
+ (
+ QApplication.translate(
+ "DBManagerPlugin", "Rows (counted):"
+ ),
+ (
+ self.table.rowCount
+ if self.table.rowCount is not None
+ else QApplication.translate(
+ "DBManagerPlugin",
+ 'Unknown (find out)',
+ )
+ ),
+ )
+ )
if table_priv[1]:
privileges.append("insert")
@@ -94,31 +150,62 @@ def generalInfo(self):
privileges.append("update")
if table_priv[3]:
privileges.append("delete")
- priv_string = ", ".join(privileges) if len(privileges) > 0 else QApplication.translate("DBManagerPlugin",
- ' This user has no privileges!')
- tbl.append((QApplication.translate("DBManagerPlugin", "Privileges:"), priv_string))
+ priv_string = (
+ ", ".join(privileges)
+ if len(privileges) > 0
+ else QApplication.translate(
+ "DBManagerPlugin", " This user has no privileges!"
+ )
+ )
+ tbl.append(
+ (QApplication.translate("DBManagerPlugin", "Privileges:"), priv_string)
+ )
ret.append(HtmlTable(tbl))
if schema_priv is not None and schema_priv[1]:
- if table_priv[0] and not table_priv[1] and not table_priv[2] and not table_priv[3]:
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " This user has read-only privileges.")))
+ if (
+ table_priv[0]
+ and not table_priv[1]
+ and not table_priv[2]
+ and not table_priv[3]
+ ):
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " This user has read-only privileges.",
+ )
+ )
+ )
if not self.table.isView:
if self.table.rowCount is not None:
- if abs(self.table.estimatedRowCount - self.table.rowCount) > 1 and \
- (self.table.estimatedRowCount > 2 * self.table.rowCount
- or self.table.rowCount > 2 * self.table.estimatedRowCount):
- ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
- " There's a significant difference between estimated and real row count. "
- 'Consider running VACUUM ANALYZE.')))
+ if abs(self.table.estimatedRowCount - self.table.rowCount) > 1 and (
+ self.table.estimatedRowCount > 2 * self.table.rowCount
+ or self.table.rowCount > 2 * self.table.estimatedRowCount
+ ):
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " There's a significant difference between estimated and real row count. "
+ 'Consider running VACUUM ANALYZE.',
+ )
+ )
+ )
# primary key defined?
if not self.table.isView:
if len([fld for fld in self.table.fields() if fld.primaryKey]) <= 0:
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " No primary key defined for this table!")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " No primary key defined for this table!",
+ )
+ )
+ )
return ret
@@ -133,23 +220,41 @@ def getSpatialInfo(self):
(QApplication.translate("DBManagerPlugin", "Library:"), info[0]),
(QApplication.translate("DBManagerPlugin", "Scripts:"), info[3]),
("GEOS:", info[1]),
- ("Proj:", info[2])
+ ("Proj:", info[2]),
]
ret.append(HtmlTable(tbl))
if info[1] is not None and info[1] != info[2]:
- ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
- " Version of installed scripts doesn't match version of released scripts!\n"
- "This is probably a result of incorrect PostGIS upgrade.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " Version of installed scripts doesn't match version of released scripts!\n"
+ "This is probably a result of incorrect PostGIS upgrade.",
+ )
+ )
+ )
if not self.db.connector.has_geometry_columns:
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " geometry_columns table doesn't exist!\n"
- "This table is essential for many GIS applications for enumeration of tables.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " geometry_columns table doesn't exist!\n"
+ "This table is essential for many GIS applications for enumeration of tables.",
+ )
+ )
+ )
elif not self.db.connector.has_geometry_columns_access:
- ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
- " This user doesn't have privileges to read contents of geometry_columns table!\n"
- "This table is essential for many GIS applications for enumeration of tables.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " This user doesn't have privileges to read contents of geometry_columns table!\n"
+ "This table is essential for many GIS applications for enumeration of tables.",
+ )
+ )
+ )
return ret
@@ -158,21 +263,40 @@ def fieldsDetails(self):
# define the table header
header = (
- "#", QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Type"),
- QApplication.translate("DBManagerPlugin", "Length"), QApplication.translate("DBManagerPlugin", "Null"),
- QApplication.translate("DBManagerPlugin", "Default"), QApplication.translate("DBManagerPlugin", "Comment"))
+ "#",
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Length"),
+ QApplication.translate("DBManagerPlugin", "Null"),
+ QApplication.translate("DBManagerPlugin", "Default"),
+ QApplication.translate("DBManagerPlugin", "Comment"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for fld in self.table.fields():
- char_max_len = fld.charMaxLen if fld.charMaxLen is not None and fld.charMaxLen != -1 else ""
+ char_max_len = (
+ fld.charMaxLen
+ if fld.charMaxLen is not None and fld.charMaxLen != -1
+ else ""
+ )
is_null_txt = "N" if fld.notNull else "Y"
# make primary key field underlined
attrs = {"class": "underline"} if fld.primaryKey else None
name = HtmlTableCol(fld.name, attrs)
- tbl.append((fld.num, name, fld.type2String(), char_max_len, is_null_txt, fld.default2String(), fld.getComment()))
+ tbl.append(
+ (
+ fld.num,
+ name,
+ fld.type2String(),
+ char_max_len,
+ is_null_txt,
+ fld.default2String(),
+ fld.getComment(),
+ )
+ )
return HtmlTable(tbl, {"class": "header"})
@@ -185,26 +309,42 @@ def triggersDetails(self):
tbl = []
# define the table header
header = (
- QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Function"),
- QApplication.translate("DBManagerPlugin", "Type"), QApplication.translate("DBManagerPlugin", "Enabled"))
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Function"),
+ QApplication.translate("DBManagerPlugin", "Type"),
+ QApplication.translate("DBManagerPlugin", "Enabled"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for trig in self.table.triggers():
- name = '%(name)s (%(action)s)' % {"name": trig.name,
- "action": "delete"}
-
- (enabled, action) = (QApplication.translate("DBManagerPlugin", "Yes"), "disable") if trig.enabled else (
- QApplication.translate("DBManagerPlugin", "No"), "enable")
- txt_enabled = '%(enabled)s (%(action)s)' % {
- "name": trig.name, "action": action, "enabled": enabled}
+ name = (
+ '{name} ({action})'.format(
+ name=trig.name, action="delete"
+ )
+ )
+
+ (enabled, action) = (
+ (QApplication.translate("DBManagerPlugin", "Yes"), "disable")
+ if trig.enabled
+ else (QApplication.translate("DBManagerPlugin", "No"), "enable")
+ )
+ txt_enabled = '{enabled} ({action})'.format(
+ name=trig.name, action=action, enabled=enabled
+ )
tbl.append((name, trig.function, trig.type2String(), txt_enabled))
ret.append(HtmlTable(tbl, {"class": "header"}))
- ret.append(HtmlParagraph(QApplication.translate("DBManagerPlugin",
- 'Enable all triggers / Disable all triggers')))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ 'Enable all triggers / Disable all triggers',
+ )
+ )
+ )
return ret
def rulesDetails(self):
@@ -214,13 +354,16 @@ def rulesDetails(self):
tbl = []
# define the table header
header = (
- QApplication.translate("DBManagerPlugin", "Name"), QApplication.translate("DBManagerPlugin", "Definition"))
+ QApplication.translate("DBManagerPlugin", "Name"),
+ QApplication.translate("DBManagerPlugin", "Definition"),
+ )
tbl.append(HtmlTableHeader(header))
# add table contents
for rule in self.table.rules():
- name = '%(name)s (%(action)s)' % {"name": rule.name,
- "action": "delete"}
+ name = '{name} ({action})'.format(
+ name=rule.name, action="delete"
+ )
tbl.append((name, rule.definition))
return HtmlTable(tbl, {"class": "header"})
@@ -233,7 +376,11 @@ def getTableInfo(self):
if rules_details is None:
pass
else:
- ret.append(HtmlSection(QApplication.translate("DBManagerPlugin", 'Rules'), rules_details))
+ ret.append(
+ HtmlSection(
+ QApplication.translate("DBManagerPlugin", "Rules"), rules_details
+ )
+ )
return ret
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugin.py b/python/plugins/db_manager/db_plugins/postgis/plugin.py
index 7e276251bacd..f294c7d579bd 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugin.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugin.py
@@ -21,18 +21,27 @@
# this will disable the dbplugin if the connector raise an ImportError
from .connector import PostGisDBConnector
-from qgis.PyQt.QtCore import (
- Qt,
- QRegularExpression,
- QCoreApplication
-)
+from qgis.PyQt.QtCore import Qt, QRegularExpression, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QApplication, QMessageBox
from qgis.core import Qgis, QgsApplication, QgsSettings
from qgis.gui import QgsMessageBar
-from ..plugin import ConnectionError, InvalidDataException, DBPlugin, Database, Schema, Table, VectorTable, RasterTable, \
- TableField, TableConstraint, TableIndex, TableTrigger, TableRule
+from ..plugin import (
+ ConnectionError,
+ InvalidDataException,
+ DBPlugin,
+ Database,
+ Schema,
+ Table,
+ VectorTable,
+ RasterTable,
+ TableField,
+ TableConstraint,
+ TableIndex,
+ TableTrigger,
+ TableRule,
+)
import re
@@ -49,19 +58,19 @@ def icon(self):
@classmethod
def typeName(self):
- return 'postgis'
+ return "postgis"
@classmethod
def typeNameString(self):
- return QCoreApplication.translate('db_manager', 'PostGIS')
+ return QCoreApplication.translate("db_manager", "PostGIS")
@classmethod
def providerName(self):
- return 'postgres'
+ return "postgres"
@classmethod
def connectionSettingsKey(self):
- return '/PostgreSQL/connections'
+ return "/PostgreSQL/connections"
def databasesFactory(self, connection, uri):
return PGDatabase(connection, uri)
@@ -69,17 +78,31 @@ def databasesFactory(self, connection, uri):
def connect(self, parent=None):
conn_name = self.connectionName()
settings = QgsSettings()
- settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), conn_name))
+ settings.beginGroup(f"/{self.connectionSettingsKey()}/{conn_name}")
if not settings.contains("database"): # non-existent entry?
- raise InvalidDataException(self.tr('There is no defined database connection "{0}".').format(conn_name))
+ raise InvalidDataException(
+ self.tr('There is no defined database connection "{0}".').format(
+ conn_name
+ )
+ )
from qgis.core import QgsDataSourceUri
uri = QgsDataSourceUri()
- settingsList = ["service", "host", "port", "database", "username", "password", "authcfg"]
- service, host, port, database, username, password, authcfg = (settings.value(x, "", type=str) for x in settingsList)
+ settingsList = [
+ "service",
+ "host",
+ "port",
+ "database",
+ "username",
+ "password",
+ "authcfg",
+ ]
+ service, host, port, database, username, password, authcfg = (
+ settings.value(x, "", type=str) for x in settingsList
+ )
useEstimatedMetadata = settings.value("estimatedMetadata", False, type=bool)
try:
@@ -89,13 +112,15 @@ def connect(self, parent=None):
settings.endGroup()
- if hasattr(authcfg, 'isNull') and authcfg.isNull():
- authcfg = ''
+ if hasattr(authcfg, "isNull") and authcfg.isNull():
+ authcfg = ""
if service:
uri.setConnection(service, database, username, password, sslmode, authcfg)
else:
- uri.setConnection(host, port, database, username, password, sslmode, authcfg)
+ uri.setConnection(
+ host, port, database, username, password, sslmode, authcfg
+ )
uri.setUseEstimatedMetadata(useEstimatedMetadata)
@@ -118,6 +143,7 @@ def dataTablesFactory(self, row, db, schema=None):
def info(self):
from .info_model import PGDatabaseInfo
+
return PGDatabaseInfo(self)
def vectorTablesFactory(self, row, db, schema=None):
@@ -148,17 +174,24 @@ def registerDatabaseActions(self, mainWindow):
mainWindow.registerAction(separator, self.tr("&Table"))
action = QAction(self.tr("Run &Vacuum Analyze"), self)
- mainWindow.registerAction(action, self.tr("&Table"), self.runVacuumAnalyzeActionSlot)
+ mainWindow.registerAction(
+ action, self.tr("&Table"), self.runVacuumAnalyzeActionSlot
+ )
action = QAction(self.tr("Run &Refresh Materialized View"), self)
- mainWindow.registerAction(action, self.tr("&Table"), self.runRefreshMaterializedViewSlot)
+ mainWindow.registerAction(
+ action, self.tr("&Table"), self.runRefreshMaterializedViewSlot
+ )
def runVacuumAnalyzeActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, Table) or item.isView:
- parent.infoBar.pushMessage(self.tr("Select a table for vacuum analyze."), Qgis.MessageLevel.Info,
- parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ self.tr("Select a table for vacuum analyze."),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -168,9 +201,12 @@ def runVacuumAnalyzeActionSlot(self, item, action, parent):
def runRefreshMaterializedViewSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
- if not isinstance(item, PGTable) or item._relationType != 'm':
- parent.infoBar.pushMessage(self.tr("Select a materialized view for refresh."), Qgis.MessageLevel.Info,
- parent.iface.messageTimeout())
+ if not isinstance(item, PGTable) or item._relationType != "m":
+ parent.infoBar.pushMessage(
+ self.tr("Select a materialized view for refresh."),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -198,8 +234,16 @@ class PGTable(Table):
def __init__(self, row, db, schema=None):
Table.__init__(self, db, schema)
- self.name, schema_name, self._relationType, self.owner, self.estimatedRowCount, self.pages, self.comment = row
- self.isView = self._relationType in {'v', 'm'}
+ (
+ self.name,
+ schema_name,
+ self._relationType,
+ self.owner,
+ self.estimatedRowCount,
+ self.pages,
+ self.comment,
+ ) = row
+ self.isView = self._relationType in {"v", "m"}
self.estimatedRowCount = int(self.estimatedRowCount)
def runVacuumAnalyze(self):
@@ -210,7 +254,9 @@ def runVacuumAnalyze(self):
def runRefreshMaterializedView(self):
self.aboutToChange.emit()
- self.database().connector.runRefreshMaterializedView((self.schemaName(), self.name))
+ self.database().connector.runRefreshMaterializedView(
+ (self.schemaName(), self.name)
+ )
# TODO: change only this item, not re-create all the tables in the schema/database
self.schema().refresh() if self.schema() else self.database().refresh()
@@ -223,7 +269,7 @@ def runAction(self, action):
return True
elif action.startswith("rule/"):
- parts = action.split('/')
+ parts = action.split("/")
rule_name = parts[1]
rule_action = parts[2]
@@ -232,15 +278,24 @@ def runAction(self, action):
QApplication.restoreOverrideCursor()
try:
- if QMessageBox.question(None, self.tr("Table rule"), msg,
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ if (
+ QMessageBox.question(
+ None,
+ self.tr("Table rule"),
+ msg,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return False
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
if rule_action == "delete":
self.aboutToChange.emit()
- self.database().connector.deleteTableRule(rule_name, (self.schemaName(), self.name))
+ self.database().connector.deleteTableRule(
+ rule_name, (self.schemaName(), self.name)
+ )
self.refreshRules()
return True
@@ -282,7 +337,9 @@ def tableDataModel(self, parent):
def delete(self):
self.aboutToChange.emit()
if self.isView:
- ret = self.database().connector.deleteView((self.schemaName(), self.name), self._relationType == 'm')
+ ret = self.database().connector.deleteView(
+ (self.schemaName(), self.name), self._relationType == "m"
+ )
else:
ret = self.database().connector.deleteTable((self.schemaName(), self.name))
if not ret:
@@ -308,7 +365,7 @@ def runAction(self, action):
return VectorTable.runAction(self, action)
def geometryType(self):
- """ Returns the proper WKT type.
+ """Returns the proper WKT type.
PostGIS records type like this:
| WKT Type | geomType | geomDim |
|--------------|-------------|---------|
@@ -331,8 +388,15 @@ class PGRasterTable(PGTable, RasterTable):
def __init__(self, row, db, schema=None):
PGTable.__init__(self, row[:-6], db, schema)
RasterTable.__init__(self, db, schema)
- self.geomColumn, self.pixelType, self.pixelSizeX, self.pixelSizeY, self.isExternal, self.srid = row[-6:]
- self.geomType = 'RASTER'
+ (
+ self.geomColumn,
+ self.pixelType,
+ self.pixelSizeX,
+ self.pixelSizeY,
+ self.isExternal,
+ self.srid,
+ ) = row[-6:]
+ self.geomType = "RASTER"
def info(self):
from .info_model import PGRasterTableInfo
@@ -344,41 +408,56 @@ def uri(self, uri=None):
if not uri:
uri = self.database().uri()
- service = ('service=\'%s\'' % uri.service()) if uri.service() else ''
- dbname = ('dbname=\'%s\'' % uri.database()) if uri.database() else ''
- host = ('host=%s' % uri.host()) if uri.host() else ''
- user = ('user=%s' % uri.username()) if uri.username() else ''
- passw = ('password=%s' % uri.password()) if uri.password() else ''
- port = ('port=%s' % uri.port()) if uri.port() else ''
+ service = ("service='%s'" % uri.service()) if uri.service() else ""
+ dbname = ("dbname='%s'" % uri.database()) if uri.database() else ""
+ host = ("host=%s" % uri.host()) if uri.host() else ""
+ user = ("user=%s" % uri.username()) if uri.username() else ""
+ passw = ("password=%s" % uri.password()) if uri.password() else ""
+ port = ("port=%s" % uri.port()) if uri.port() else ""
- schema = self.schemaName() if self.schemaName() else 'public'
- table = '"%s"."%s"' % (schema, self.name)
+ schema = self.schemaName() if self.schemaName() else "public"
+ table = f'"{schema}"."{self.name}"'
if not dbname:
# postgresraster provider *requires* a dbname
connector = self.database().connector
r = connector._execute(None, "SELECT current_database()")
- dbname = ('dbname=\'%s\'' % connector._fetchone(r)[0])
+ dbname = "dbname='%s'" % connector._fetchone(r)[0]
connector._close_cursor(r)
# Find first raster field
- col = ''
+ col = ""
for fld in self.fields():
if fld.dataType == "raster":
- col = 'column=\'%s\'' % fld.name
+ col = "column='%s'" % fld.name
break
- uri = '%s %s %s %s %s %s %s table=%s' % \
- (service, dbname, host, user, passw, port, col, table)
+ uri = "{} {} {} {} {} {} {} table={}".format(
+ service,
+ dbname,
+ host,
+ user,
+ passw,
+ port,
+ col,
+ table,
+ )
return uri
def mimeUri(self):
- uri = "raster:postgresraster:{}:{}".format(self.name, re.sub(":", r"\:", self.uri()))
+ uri = "raster:postgresraster:{}:{}".format(
+ self.name, re.sub(":", r"\:", self.uri())
+ )
return uri
def toMapLayer(self, geometryType=None, crs=None):
- from qgis.core import QgsRasterLayer, QgsContrastEnhancement, QgsDataSourceUri, QgsCredentials
+ from qgis.core import (
+ QgsRasterLayer,
+ QgsContrastEnhancement,
+ QgsDataSourceUri,
+ QgsCredentials,
+ )
rl = QgsRasterLayer(self.uri(), self.name, "postgresraster")
if not rl.isValid():
@@ -389,7 +468,9 @@ def toMapLayer(self, geometryType=None, crs=None):
password = uri.password()
for i in range(3):
- (ok, username, password) = QgsCredentials.instance().get(conninfo, username, password, err)
+ (ok, username, password) = QgsCredentials.instance().get(
+ conninfo, username, password, err
+ )
if ok:
uri.setUsername(username)
uri.setPassword(password)
@@ -398,7 +479,9 @@ def toMapLayer(self, geometryType=None, crs=None):
break
if rl.isValid():
- rl.setContrastEnhancement(QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum)
+ rl.setContrastEnhancement(
+ QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum
+ )
return rl
@@ -406,7 +489,17 @@ class PGTableField(TableField):
def __init__(self, row, table):
TableField.__init__(self, table)
- self.num, self.name, self.dataType, self.charMaxLen, self.modifier, self.notNull, self.hasDefault, self.default, typeStr = row
+ (
+ self.num,
+ self.name,
+ self.dataType,
+ self.charMaxLen,
+ self.modifier,
+ self.notNull,
+ self.hasDefault,
+ self.default,
+ typeStr,
+ ) = row
self.primaryKey = False
# get modifier (e.g. "precision,scale") from formatted type string
@@ -428,9 +521,13 @@ def getComment(self):
"""Returns the comment for a field"""
tab = self.table()
# SQL Query checking if a comment exists for the field
- sql_cpt = "Select count(*) from pg_description pd, pg_class pc, pg_attribute pa where relname = '%s' and attname = '%s' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum" % (tab.name, self.name)
+ sql_cpt = "Select count(*) from pg_description pd, pg_class pc, pg_attribute pa where relname = '{}' and attname = '{}' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum".format(
+ tab.name, self.name
+ )
# SQL Query that return the comment of the field
- sql = "Select pd.description from pg_description pd, pg_class pc, pg_attribute pa where relname = '%s' and attname = '%s' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum" % (tab.name, self.name)
+ sql = "Select pd.description from pg_description pd, pg_class pc, pg_attribute pa where relname = '{}' and attname = '{}' and pa.attrelid = pc.oid and pd.objoid = pc.oid and pd.objsubid = pa.attnum".format(
+ tab.name, self.name
+ )
c = tab.database().connector._execute(None, sql_cpt) # Execute Check query
res = tab.database().connector._fetchone(c)[0] # Store result
if res == 1:
@@ -440,15 +537,17 @@ def getComment(self):
tab.database().connector._close_cursor(c) # Close cursor
return res # Return comment
else:
- return ''
+ return ""
class PGTableConstraint(TableConstraint):
def __init__(self, row, table):
TableConstraint.__init__(self, table)
- self.name, constr_type_str, self.isDefferable, self.isDeffered, columns = row[:5]
- self.columns = list(map(int, columns.split(' ')))
+ self.name, constr_type_str, self.isDefferable, self.isDeffered, columns = row[
+ :5
+ ]
+ self.columns = list(map(int, columns.split(" ")))
if constr_type_str in TableConstraint.types:
self.type = TableConstraint.types[constr_type_str]
@@ -470,7 +569,7 @@ class PGTableIndex(TableIndex):
def __init__(self, row, table):
TableIndex.__init__(self, table)
self.name, columns, self.isUnique = row
- self.columns = list(map(int, columns.split(' ')))
+ self.columns = list(map(int, columns.split(" ")))
class PGTableTrigger(TableTrigger):
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugin_test.py b/python/plugins/db_manager/db_plugins/postgis/plugin_test.py
index 9f57ff047080..f202b1354af2 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugin_test.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugin_test.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Sandro Santilli'
-__date__ = 'May 2017'
-__copyright__ = '(C) 2017, Sandro Santilli'
+__author__ = "Sandro Santilli"
+__date__ = "May 2017"
+__copyright__ = "(C) 2017, Sandro Santilli"
import os
import re
@@ -40,23 +40,23 @@ class TestDBManagerPostgisPlugin(QgisTestCase):
@classmethod
def setUpClass(self):
- self.old_pgdatabase_env = os.environ.get('PGDATABASE')
+ self.old_pgdatabase_env = os.environ.get("PGDATABASE")
# QGIS_PGTEST_DB contains the full connection string and not only the DB name!
- QGIS_PGTEST_DB = os.environ.get('QGIS_PGTEST_DB')
+ QGIS_PGTEST_DB = os.environ.get("QGIS_PGTEST_DB")
if QGIS_PGTEST_DB is not None:
test_uri = QgsDataSourceUri(QGIS_PGTEST_DB)
self.testdb = test_uri.database()
else:
- self.testdb = 'qgis_test'
- os.environ['PGDATABASE'] = self.testdb
+ self.testdb = "qgis_test"
+ os.environ["PGDATABASE"] = self.testdb
# Create temporary service file
- self.old_pgservicefile_env = os.environ.get('PGSERVICEFILE')
- self.tmpservicefile = '/tmp/qgis-test-{}-pg_service.conf'.format(os.getpid())
- os.environ['PGSERVICEFILE'] = self.tmpservicefile
+ self.old_pgservicefile_env = os.environ.get("PGSERVICEFILE")
+ self.tmpservicefile = f"/tmp/qgis-test-{os.getpid()}-pg_service.conf"
+ os.environ["PGSERVICEFILE"] = self.tmpservicefile
f = open(self.tmpservicefile, "w")
- f.write("[dbmanager]\ndbname={}\n".format(self.testdb))
+ f.write(f"[dbmanager]\ndbname={self.testdb}\n")
# TODO: add more things if PGSERVICEFILE was already set ?
f.close()
@@ -64,9 +64,9 @@ def setUpClass(self):
def tearDownClass(self):
# Restore previous env variables if needed
if self.old_pgdatabase_env:
- os.environ['PGDATABASE'] = self.old_pgdatabase_env
+ os.environ["PGDATABASE"] = self.old_pgdatabase_env
if self.old_pgservicefile_env:
- os.environ['PGSERVICEFILE'] = self.old_pgservicefile_env
+ os.environ["PGSERVICEFILE"] = self.old_pgservicefile_env
# Remove temporary service file
os.unlink(self.tmpservicefile)
@@ -81,7 +81,7 @@ def check_rasterTableURI(expected_dbname):
if tab.type == Table.RasterType:
raster_tables_count += 1
uri = tab.uri()
- m = re.search(' dbname=\'([^ ]*)\' ', uri)
+ m = re.search(" dbname='([^ ]*)' ", uri)
self.assertTrue(m)
actual_dbname = m.group(1)
self.assertEqual(actual_dbname, expected_dbname)
@@ -94,48 +94,48 @@ def check_rasterTableURI(expected_dbname):
self.assertGreaterEqual(raster_tables_count, 1)
obj = QObject() # needs to be kept alive
- obj.connectionName = lambda: 'fake'
- obj.providerName = lambda: 'postgres'
+ obj.connectionName = lambda: "fake"
+ obj.providerName = lambda: "postgres"
# Test for empty URI
# See https://github.com/qgis/QGIS/issues/24525
# and https://github.com/qgis/QGIS/issues/19005
expected_dbname = self.testdb
- os.environ['PGDATABASE'] = expected_dbname
+ os.environ["PGDATABASE"] = expected_dbname
database = PGDatabase(obj, QgsDataSourceUri())
self.assertIsInstance(database, PGDatabase)
uri = database.uri()
- self.assertEqual(uri.host(), '')
- self.assertEqual(uri.username(), '')
+ self.assertEqual(uri.host(), "")
+ self.assertEqual(uri.username(), "")
self.assertEqual(uri.database(), expected_dbname)
- self.assertEqual(uri.service(), '')
+ self.assertEqual(uri.service(), "")
check_rasterTableURI(expected_dbname)
# Test for service-only URI
# See https://github.com/qgis/QGIS/issues/24526
- os.environ['PGDATABASE'] = 'fake'
- database = PGDatabase(obj, QgsDataSourceUri('service=dbmanager'))
+ os.environ["PGDATABASE"] = "fake"
+ database = PGDatabase(obj, QgsDataSourceUri("service=dbmanager"))
self.assertIsInstance(database, PGDatabase)
uri = database.uri()
- self.assertEqual(uri.host(), '')
- self.assertEqual(uri.username(), '')
- self.assertEqual(uri.database(), '')
- self.assertEqual(uri.service(), 'dbmanager')
+ self.assertEqual(uri.host(), "")
+ self.assertEqual(uri.username(), "")
+ self.assertEqual(uri.database(), "")
+ self.assertEqual(uri.service(), "dbmanager")
check_rasterTableURI(expected_dbname)
# See https://github.com/qgis/QGIS/issues/24732
def test_unicodeInQuery(self):
- os.environ['PGDATABASE'] = self.testdb
+ os.environ["PGDATABASE"] = self.testdb
obj = QObject() # needs to be kept alive
- obj.connectionName = lambda: 'fake'
- obj.providerName = lambda: 'postgres'
+ obj.connectionName = lambda: "fake"
+ obj.providerName = lambda: "postgres"
database = PGDatabase(obj, QgsDataSourceUri())
self.assertIsInstance(database, PGDatabase)
# SQL as string literal
@@ -150,5 +150,5 @@ def test_unicodeInQuery(self):
self.assertEqual(dat, "é")
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugins/__init__.py b/python/plugins/db_manager/db_plugins/postgis/plugins/__init__.py
index aa526e4a9fcb..a881b28d1a71 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugins/__init__.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugins/__init__.py
@@ -28,10 +28,10 @@ def load(db, mainwindow):
for name in os.listdir(current_dir):
if not os.path.isdir(os.path.join(current_dir, name)):
continue
- if name in ('__pycache__'):
+ if name in ("__pycache__"):
continue
try:
- plugin_module = import_module('.'.join((__package__, name)))
+ plugin_module = import_module(".".join((__package__, name)))
except ImportError:
continue
plugin_module.load(db, mainwindow)
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugins/qgis_topoview/__init__.py b/python/plugins/db_manager/db_plugins/postgis/plugins/qgis_topoview/__init__.py
index 80c038df01b0..3b66aa9716e8 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugins/qgis_topoview/__init__.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugins/qgis_topoview/__init__.py
@@ -68,32 +68,47 @@ def run(item, action, mainwindow):
# check if the selected item is a topology schema
isTopoSchema = False
- if not hasattr(item, 'schema'):
- mainwindow.infoBar.pushMessage("Invalid topology", 'Select a topology schema to continue.', Qgis.MessageLevel.Info,
- mainwindow.iface.messageTimeout())
+ if not hasattr(item, "schema"):
+ mainwindow.infoBar.pushMessage(
+ "Invalid topology",
+ "Select a topology schema to continue.",
+ Qgis.MessageLevel.Info,
+ mainwindow.iface.messageTimeout(),
+ )
return False
if item.schema() is not None:
- sql = "SELECT srid FROM topology.topology WHERE name = %s" % quoteStr(item.schema().name)
+ sql = "SELECT srid FROM topology.topology WHERE name = %s" % quoteStr(
+ item.schema().name
+ )
res = db.executeSql(sql)
isTopoSchema = len(res) > 0
if not isTopoSchema:
- mainwindow.infoBar.pushMessage("Invalid topology",
- 'Schema "{}" is not registered in topology.topology.'.format(
- item.schema().name), Qgis.MessageLevel.Warning,
- mainwindow.iface.messageTimeout())
+ mainwindow.infoBar.pushMessage(
+ "Invalid topology",
+ 'Schema "{}" is not registered in topology.topology.'.format(
+ item.schema().name
+ ),
+ Qgis.MessageLevel.Warning,
+ mainwindow.iface.messageTimeout(),
+ )
return False
- if (res[0][0] < 0):
- mainwindow.infoBar.pushMessage("WARNING", 'Topology "{}" is registered as having a srid of {} in topology.topology, we will assume 0 (for unknown)'.format(item.schema().name, res[0]), Qgis.MessageLevel.Warning, mainwindow.iface.messageTimeout())
- toposrid = '0'
+ if res[0][0] < 0:
+ mainwindow.infoBar.pushMessage(
+ "WARNING",
+ f'Topology "{item.schema().name}" is registered as having a srid of {res[0]} in topology.topology, we will assume 0 (for unknown)',
+ Qgis.MessageLevel.Warning,
+ mainwindow.iface.messageTimeout(),
+ )
+ toposrid = "0"
else:
toposrid = str(res[0][0])
# load layers into the current project
toponame = item.schema().name
- template_dir = os.path.join(current_path, 'templates')
+ template_dir = os.path.join(current_path, "templates")
# do not refresh the canvas until all the layers are added
wasFrozen = iface.mapCanvas().isFrozen()
@@ -108,134 +123,164 @@ def run(item, action, mainwindow):
# FACES
# face mbr
- uri.setDataSource(toponame, 'face', 'mbr', '', 'face_id')
+ uri.setDataSource(toponame, "face", "mbr", "", "face_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.Polygon)
- layerFaceMbr = QgsVectorLayer(uri.uri(False), '%s.face_mbr' % toponame, provider)
- layerFaceMbr.loadNamedStyle(os.path.join(template_dir, 'face_mbr.qml'))
+ layerFaceMbr = QgsVectorLayer(
+ uri.uri(False), "%s.face_mbr" % toponame, provider
+ )
+ layerFaceMbr.loadNamedStyle(os.path.join(template_dir, "face_mbr.qml"))
face_extent = layerFaceMbr.extent()
# face geometry
- sql = 'SELECT face_id, mbr, topology.ST_GetFaceGeometry(%s,' \
- 'face_id)::geometry(polygon, %s) as geom ' \
- 'FROM %s.face WHERE face_id > 0' % \
- (quoteStr(toponame), toposrid, quoteId(toponame))
- uri.setDataSource('', '(%s\n)' % sql, 'geom', '', 'face_id')
- uri.setParam('bbox', 'mbr')
- uri.setParam('checkPrimaryKeyUnicity', '0')
+ sql = (
+ "SELECT face_id, mbr, topology.ST_GetFaceGeometry(%s,"
+ "face_id)::geometry(polygon, %s) as geom "
+ "FROM %s.face WHERE face_id > 0"
+ % (quoteStr(toponame), toposrid, quoteId(toponame))
+ )
+ uri.setDataSource("", "(%s\n)" % sql, "geom", "", "face_id")
+ uri.setParam("bbox", "mbr")
+ uri.setParam("checkPrimaryKeyUnicity", "0")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.Polygon)
- layerFaceGeom = QgsVectorLayer(uri.uri(False), '%s.face' % toponame, provider)
+ layerFaceGeom = QgsVectorLayer(uri.uri(False), "%s.face" % toponame, provider)
layerFaceGeom.setExtent(face_extent)
- layerFaceGeom.loadNamedStyle(os.path.join(template_dir, 'face.qml'))
+ layerFaceGeom.loadNamedStyle(os.path.join(template_dir, "face.qml"))
# face_seed
- sql = 'SELECT face_id, mbr, ST_PointOnSurface(' \
- 'topology.ST_GetFaceGeometry(%s,' \
- 'face_id))::geometry(point, %s) as geom ' \
- 'FROM %s.face WHERE face_id > 0' % \
- (quoteStr(toponame), toposrid, quoteId(toponame))
- uri.setDataSource('', '(%s)' % sql, 'geom', '', 'face_id')
- uri.setParam('bbox', 'mbr')
- uri.setParam('checkPrimaryKeyUnicity', '0')
+ sql = (
+ "SELECT face_id, mbr, ST_PointOnSurface("
+ "topology.ST_GetFaceGeometry(%s,"
+ "face_id))::geometry(point, %s) as geom "
+ "FROM %s.face WHERE face_id > 0"
+ % (quoteStr(toponame), toposrid, quoteId(toponame))
+ )
+ uri.setDataSource("", "(%s)" % sql, "geom", "", "face_id")
+ uri.setParam("bbox", "mbr")
+ uri.setParam("checkPrimaryKeyUnicity", "0")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.Point)
- layerFaceSeed = QgsVectorLayer(uri.uri(False), '%s.face_seed' % toponame, provider)
+ layerFaceSeed = QgsVectorLayer(
+ uri.uri(False), "%s.face_seed" % toponame, provider
+ )
layerFaceSeed.setExtent(face_extent)
- layerFaceSeed.loadNamedStyle(os.path.join(template_dir, 'face_seed.qml'))
+ layerFaceSeed.loadNamedStyle(os.path.join(template_dir, "face_seed.qml"))
# TODO: add polygon0, polygon1 and polygon2 ?
# NODES
# node
- uri.setDataSource(toponame, 'node', 'geom', '', 'node_id')
- uri.removeParam('bbox')
+ uri.setDataSource(toponame, "node", "geom", "", "node_id")
+ uri.removeParam("bbox")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.Point)
- layerNode = QgsVectorLayer(uri.uri(False), '%s.node' % toponame, provider)
- layerNode.loadNamedStyle(os.path.join(template_dir, 'node.qml'))
+ layerNode = QgsVectorLayer(uri.uri(False), "%s.node" % toponame, provider)
+ layerNode.loadNamedStyle(os.path.join(template_dir, "node.qml"))
node_extent = layerNode.extent()
# node labels
- uri.setDataSource(toponame, 'node', 'geom', '', 'node_id')
+ uri.setDataSource(toponame, "node", "geom", "", "node_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.Point)
- uri.removeParam('bbox')
- layerNodeLabel = QgsVectorLayer(uri.uri(False), '%s.node_id' % toponame, provider)
+ uri.removeParam("bbox")
+ layerNodeLabel = QgsVectorLayer(
+ uri.uri(False), "%s.node_id" % toponame, provider
+ )
layerNodeLabel.setExtent(node_extent)
- layerNodeLabel.loadNamedStyle(os.path.join(template_dir, 'node_label.qml'))
+ layerNodeLabel.loadNamedStyle(os.path.join(template_dir, "node_label.qml"))
# EDGES
# edge
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerEdge = QgsVectorLayer(uri.uri(False), '%s.edge' % toponame, provider)
+ uri.removeParam("bbox")
+ layerEdge = QgsVectorLayer(uri.uri(False), "%s.edge" % toponame, provider)
edge_extent = layerEdge.extent()
# directed edge
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerDirectedEdge = QgsVectorLayer(uri.uri(False), '%s.directed_edge' % toponame, provider)
+ uri.removeParam("bbox")
+ layerDirectedEdge = QgsVectorLayer(
+ uri.uri(False), "%s.directed_edge" % toponame, provider
+ )
layerDirectedEdge.setExtent(edge_extent)
- layerDirectedEdge.loadNamedStyle(os.path.join(template_dir, 'edge.qml'))
+ layerDirectedEdge.loadNamedStyle(os.path.join(template_dir, "edge.qml"))
# edge labels
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerEdgeLabel = QgsVectorLayer(uri.uri(False), '%s.edge_id' % toponame, provider)
+ uri.removeParam("bbox")
+ layerEdgeLabel = QgsVectorLayer(
+ uri.uri(False), "%s.edge_id" % toponame, provider
+ )
layerEdgeLabel.setExtent(edge_extent)
- layerEdgeLabel.loadNamedStyle(os.path.join(template_dir, 'edge_label.qml'))
+ layerEdgeLabel.loadNamedStyle(os.path.join(template_dir, "edge_label.qml"))
# face_left
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerFaceLeft = QgsVectorLayer(uri.uri(False), '%s.face_left' % toponame, provider)
+ uri.removeParam("bbox")
+ layerFaceLeft = QgsVectorLayer(
+ uri.uri(False), "%s.face_left" % toponame, provider
+ )
layerFaceLeft.setExtent(edge_extent)
- layerFaceLeft.loadNamedStyle(os.path.join(template_dir, 'face_left.qml'))
+ layerFaceLeft.loadNamedStyle(os.path.join(template_dir, "face_left.qml"))
# face_right
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerFaceRight = QgsVectorLayer(uri.uri(False), '%s.face_right' % toponame, provider)
+ uri.removeParam("bbox")
+ layerFaceRight = QgsVectorLayer(
+ uri.uri(False), "%s.face_right" % toponame, provider
+ )
layerFaceRight.setExtent(edge_extent)
- layerFaceRight.loadNamedStyle(os.path.join(template_dir, 'face_right.qml'))
+ layerFaceRight.loadNamedStyle(os.path.join(template_dir, "face_right.qml"))
# next_left
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerNextLeft = QgsVectorLayer(uri.uri(False), '%s.next_left' % toponame, provider)
+ uri.removeParam("bbox")
+ layerNextLeft = QgsVectorLayer(
+ uri.uri(False), "%s.next_left" % toponame, provider
+ )
layerNextLeft.setExtent(edge_extent)
- layerNextLeft.loadNamedStyle(os.path.join(template_dir, 'next_left.qml'))
+ layerNextLeft.loadNamedStyle(os.path.join(template_dir, "next_left.qml"))
# next_right
- uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id')
+ uri.setDataSource(toponame, "edge_data", "geom", "", "edge_id")
uri.setSrid(toposrid)
uri.setWkbType(QgsWkbTypes.Type.LineString)
- uri.removeParam('bbox')
- layerNextRight = QgsVectorLayer(uri.uri(False), '%s.next_right' % toponame, provider)
+ uri.removeParam("bbox")
+ layerNextRight = QgsVectorLayer(
+ uri.uri(False), "%s.next_right" % toponame, provider
+ )
layerNextRight.setExtent(edge_extent)
- layerNextRight.loadNamedStyle(os.path.join(template_dir, 'next_right.qml'))
+ layerNextRight.loadNamedStyle(os.path.join(template_dir, "next_right.qml"))
# Add layers to the layer tree
faceLayers = [layerFaceMbr, layerFaceGeom, layerFaceSeed]
nodeLayers = [layerNode, layerNodeLabel]
- edgeLayers = [layerEdge, layerDirectedEdge, layerEdgeLabel, layerFaceLeft, layerFaceRight, layerNextLeft, layerNextRight]
+ edgeLayers = [
+ layerEdge,
+ layerDirectedEdge,
+ layerEdgeLabel,
+ layerFaceLeft,
+ layerFaceRight,
+ layerNextLeft,
+ layerNextRight,
+ ]
QgsProject.instance().addMapLayers(faceLayers, False)
QgsProject.instance().addMapLayers(nodeLayers, False)
@@ -243,19 +288,19 @@ def run(item, action, mainwindow):
# Organize layers in groups
- groupFaces = QgsLayerTreeGroup('Faces')
+ groupFaces = QgsLayerTreeGroup("Faces")
for layer in faceLayers:
nodeLayer = groupFaces.addLayer(layer)
nodeLayer.setItemVisibilityChecked(False)
nodeLayer.setExpanded(False)
- groupNodes = QgsLayerTreeGroup('Nodes')
+ groupNodes = QgsLayerTreeGroup("Nodes")
for layer in nodeLayers:
nodeLayer = groupNodes.addLayer(layer)
nodeLayer.setItemVisibilityChecked(False)
nodeLayer.setExpanded(False)
- groupEdges = QgsLayerTreeGroup('Edges')
+ groupEdges = QgsLayerTreeGroup("Edges")
for layer in edgeLayers:
nodeLayer = groupEdges.addLayer(layer)
nodeLayer.setItemVisibilityChecked(False)
@@ -295,7 +340,7 @@ def run(item, action, mainwindow):
# Set canvas extent to topology extent, if not yet initialized
canvas = iface.mapCanvas()
- if (canvas.fullExtent().isNull()):
+ if canvas.fullExtent().isNull():
ext = node_extent
ext.combineExtentWith(edge_extent)
# Grow by 1/20 of largest side
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/__init__.py b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/__init__.py
index 4320453779b4..556ce859bcdc 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/__init__.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/__init__.py
@@ -30,8 +30,12 @@
def load(db, mainwindow):
# add the action to the DBManager menu
- action = QAction(QIcon(), QApplication.translate("DBManagerPlugin", "&Change Logging…"), db)
- mainwindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"), run)
+ action = QAction(
+ QIcon(), QApplication.translate("DBManagerPlugin", "&Change Logging…"), db
+ )
+ mainwindow.registerAction(
+ action, QApplication.translate("DBManagerPlugin", "&Table"), run
+ )
# The run function is called once the user clicks on the action TopoViewer
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/dlg_versioning.py b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/dlg_versioning.py
index 41813bcb35a1..068ba08503b9 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/dlg_versioning.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/dlg_versioning.py
@@ -28,7 +28,7 @@
from .....dlg_db_error import DlgDbError
from ....plugin import BaseError, Table
-Ui_DlgVersioning, _ = uic.loadUiType(Path(__file__).parent / 'DlgVersioining.ui')
+Ui_DlgVersioning, _ = uic.loadUiType(Path(__file__).parent / "DlgVersioining.ui")
class DlgVersioning(QDialog, Ui_DlgVersioning):
@@ -75,7 +75,7 @@ def populateSchemas(self):
index = -1
for schema in self.schemas:
self.cboSchema.addItem(schema.name)
- if hasattr(self.item, 'schema') and schema.name == self.item.schema().name:
+ if hasattr(self.item, "schema") and schema.name == self.item.schema().name:
index = self.cboSchema.count() - 1
self.cboSchema.setCurrentIndex(index)
@@ -100,12 +100,15 @@ def populateTables(self):
self.cboTable.addItem(table.name)
def get_escaped_name(self, schema, table, suffix):
- name = self.db.connector.quoteId("%s%s" % (table, suffix))
+ name = self.db.connector.quoteId(f"{table}{suffix}")
schema_name = self.db.connector.quoteId(schema) if schema else None
- return "%s.%s" % (schema_name, name) if schema_name else name
+ return f"{schema_name}.{name}" if schema_name else name
def updateSql(self):
- if self.cboTable.currentIndex() < 0 or len(self.tables) < self.cboTable.currentIndex():
+ if (
+ self.cboTable.currentIndex() < 0
+ or len(self.tables) < self.cboTable.currentIndex()
+ ):
return
self.table = self.tables[self.cboTable.currentIndex()]
@@ -124,7 +127,10 @@ def updateSql(self):
for constr in self.table.constraints():
if constr.type == constr.TypePrimaryKey:
self.origPkeyName = self.db.connector.quoteId(constr.name)
- self.colOrigPkey = [self.db.connector.quoteId(x_y[1].name) for x_y in iter(list(constr.fields().items()))]
+ self.colOrigPkey = [
+ self.db.connector.quoteId(x_y[1].name)
+ for x_y in iter(list(constr.fields().items()))
+ ]
break
if self.colOrigPkey is None:
@@ -140,11 +146,19 @@ def updateSql(self):
self.colOrigPkey = self.colOrigPkey[0]
# define view, function, rule and trigger names
- self.view = self.get_escaped_name(self.table.schemaName(), self.table.name, "_current")
-
- self.func_at_time = self.get_escaped_name(self.table.schemaName(), self.table.name, "_at_time")
- self.func_update = self.get_escaped_name(self.table.schemaName(), self.table.name, "_update")
- self.func_insert = self.get_escaped_name(self.table.schemaName(), self.table.name, "_insert")
+ self.view = self.get_escaped_name(
+ self.table.schemaName(), self.table.name, "_current"
+ )
+
+ self.func_at_time = self.get_escaped_name(
+ self.table.schemaName(), self.table.name, "_at_time"
+ )
+ self.func_update = self.get_escaped_name(
+ self.table.schemaName(), self.table.name, "_update"
+ )
+ self.func_insert = self.get_escaped_name(
+ self.table.schemaName(), self.table.name, "_insert"
+ )
self.rule_del = self.get_escaped_name(None, self.table.name, "_del")
self.trigger_update = self.get_escaped_name(None, self.table.name, "_update")
@@ -166,7 +180,7 @@ def updateSql(self):
# if self.current:
sql.append(self.sql_updatesView())
- self.txtSql.setPlainText('\n\n'.join(sql))
+ self.txtSql.setPlainText("\n\n".join(sql))
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(True)
return sql
@@ -176,18 +190,27 @@ def showHelp(self):
QMessageBox.information(self, "Help", helpText)
def sql_alterTable(self):
- return "ALTER TABLE %s ADD %s serial, ADD %s timestamp default '-infinity', ADD %s timestamp, ADD %s varchar;" % (
- self.schematable, self.colPkey, self.colStart, self.colEnd, self.colUser)
+ return "ALTER TABLE {} ADD {} serial, ADD {} timestamp default '-infinity', ADD {} timestamp, ADD {} varchar;".format(
+ self.schematable, self.colPkey, self.colStart, self.colEnd, self.colUser
+ )
def sql_setPkey(self):
- return "ALTER TABLE %s DROP CONSTRAINT %s, ADD PRIMARY KEY (%s);" % (
- self.schematable, self.origPkeyName, self.colPkey)
+ return "ALTER TABLE {} DROP CONSTRAINT {}, ADD PRIMARY KEY ({});".format(
+ self.schematable, self.origPkeyName, self.colPkey
+ )
def sql_currentView(self):
cols = self.colPkey + "," + ",".join(self.columns)
- return "CREATE VIEW %(view)s AS SELECT %(cols)s FROM %(schematable)s WHERE %(end)s IS NULL;" % \
- {'view': self.view, 'cols': cols, 'schematable': self.schematable, 'end': self.colEnd}
+ return (
+ "CREATE VIEW %(view)s AS SELECT %(cols)s FROM %(schematable)s WHERE %(end)s IS NULL;"
+ % {
+ "view": self.view,
+ "cols": cols,
+ "schematable": self.schematable,
+ "end": self.colEnd,
+ }
+ )
def sql_functions(self):
cols = ",".join(self.columns)
@@ -195,80 +218,99 @@ def sql_functions(self):
old_cols = ",".join("OLD." + x for x in self.columns)
sql = """
-CREATE OR REPLACE FUNCTION %(func_at_time)s(timestamp)
-RETURNS SETOF %(view)s AS
+CREATE OR REPLACE FUNCTION {func_at_time}(timestamp)
+RETURNS SETOF {view} AS
$$
-SELECT %(all_cols)s FROM %(schematable)s WHERE
- ( SELECT CASE WHEN %(end)s IS NULL THEN (%(start)s <= $1) ELSE (%(start)s <= $1 AND %(end)s > $1) END );
+SELECT {all_cols} FROM {schematable} WHERE
+ ( SELECT CASE WHEN {end} IS NULL THEN ({start} <= $1) ELSE ({start} <= $1 AND {end} > $1) END );
$$
LANGUAGE 'sql';
-CREATE OR REPLACE FUNCTION %(func_update)s()
+CREATE OR REPLACE FUNCTION {func_update}()
RETURNS TRIGGER AS
$$
BEGIN
- IF OLD.%(end)s IS NOT NULL THEN
+ IF OLD.{end} IS NOT NULL THEN
RETURN NULL;
END IF;
- IF NEW.%(end)s IS NULL THEN
- INSERT INTO %(schematable)s (%(cols)s, %(start)s, %(end)s) VALUES (%(oldcols)s, OLD.%(start)s, current_timestamp);
- NEW.%(start)s = current_timestamp;
- NEW.%(user)s = current_user;
+ IF NEW.{end} IS NULL THEN
+ INSERT INTO {schematable} ({cols}, {start}, {end}) VALUES ({oldcols}, OLD.{start}, current_timestamp);
+ NEW.{start} = current_timestamp;
+ NEW.{user} = current_user;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
-CREATE OR REPLACE FUNCTION %(func_insert)s()
+CREATE OR REPLACE FUNCTION {func_insert}()
RETURNS trigger AS
$$
BEGIN
- if NEW.%(start)s IS NULL then
- NEW.%(start)s = now();
- NEW.%(end)s = null;
- NEW.%(user)s = current_user;
+ if NEW.{start} IS NULL then
+ NEW.{start} = now();
+ NEW.{end} = null;
+ NEW.{user} = current_user;
end if;
RETURN NEW;
END;
$$
-LANGUAGE 'plpgsql';""" % {'view': self.view, 'schematable': self.schematable, 'cols': cols, 'oldcols': old_cols,
- 'start': self.colStart, 'end': self.colEnd, 'user': self.colUser, 'func_at_time': self.func_at_time,
- 'all_cols': all_cols, 'func_update': self.func_update, 'func_insert': self.func_insert}
+LANGUAGE 'plpgsql';""".format(
+ view=self.view,
+ schematable=self.schematable,
+ cols=cols,
+ oldcols=old_cols,
+ start=self.colStart,
+ end=self.colEnd,
+ user=self.colUser,
+ func_at_time=self.func_at_time,
+ all_cols=all_cols,
+ func_update=self.func_update,
+ func_insert=self.func_insert,
+ )
return sql
def sql_triggers(self):
return """
-CREATE RULE %(rule_del)s AS ON DELETE TO %(schematable)s
-DO INSTEAD UPDATE %(schematable)s SET %(end)s = current_timestamp WHERE %(pkey)s = OLD.%(pkey)s AND %(end)s IS NULL;
-
-CREATE TRIGGER %(trigger_update)s BEFORE UPDATE ON %(schematable)s
-FOR EACH ROW EXECUTE PROCEDURE %(func_update)s();
-
-CREATE TRIGGER %(trigger_insert)s BEFORE INSERT ON %(schematable)s
-FOR EACH ROW EXECUTE PROCEDURE %(func_insert)s();""" % \
- {'rule_del': self.rule_del, 'trigger_update': self.trigger_update, 'trigger_insert': self.trigger_insert,
- 'func_update': self.func_update, 'func_insert': self.func_insert, 'schematable': self.schematable,
- 'pkey': self.colPkey, 'end': self.colEnd}
+CREATE RULE {rule_del} AS ON DELETE TO {schematable}
+DO INSTEAD UPDATE {schematable} SET {end} = current_timestamp WHERE {pkey} = OLD.{pkey} AND {end} IS NULL;
+
+CREATE TRIGGER {trigger_update} BEFORE UPDATE ON {schematable}
+FOR EACH ROW EXECUTE PROCEDURE {func_update}();
+
+CREATE TRIGGER {trigger_insert} BEFORE INSERT ON {schematable}
+FOR EACH ROW EXECUTE PROCEDURE {func_insert}();""".format(
+ rule_del=self.rule_del,
+ trigger_update=self.trigger_update,
+ trigger_insert=self.trigger_insert,
+ func_update=self.func_update,
+ func_insert=self.func_insert,
+ schematable=self.schematable,
+ pkey=self.colPkey,
+ end=self.colEnd,
+ )
def sql_updatesView(self):
cols = ",".join(self.columns)
return_cols = self.colPkey + "," + ",".join(self.columns)
new_cols = ",".join("NEW." + x for x in self.columns)
- assign_cols = ",".join("%s = NEW.%s" % (x, x) for x in self.columns)
+ assign_cols = ",".join(f"{x} = NEW.{x}" for x in self.columns)
return """
-CREATE OR REPLACE RULE "_DELETE" AS ON DELETE TO %(view)s DO INSTEAD
- DELETE FROM %(schematable)s WHERE %(origpkey)s = old.%(origpkey)s;
-CREATE OR REPLACE RULE "_INSERT" AS ON INSERT TO %(view)s DO INSTEAD
- INSERT INTO %(schematable)s (%(cols)s) VALUES (%(newcols)s) RETURNING %(return_cols)s;
-CREATE OR REPLACE RULE "_UPDATE" AS ON UPDATE TO %(view)s DO INSTEAD
- UPDATE %(schematable)s SET %(assign)s WHERE %(origpkey)s = NEW.%(origpkey)s;""" % {'view': self.view,
- 'schematable': self.schematable,
- 'cols': cols, 'newcols': new_cols,
- 'return_cols': return_cols,
- 'assign': assign_cols,
- 'origpkey': self.colOrigPkey}
+CREATE OR REPLACE RULE "_DELETE" AS ON DELETE TO {view} DO INSTEAD
+ DELETE FROM {schematable} WHERE {origpkey} = old.{origpkey};
+CREATE OR REPLACE RULE "_INSERT" AS ON INSERT TO {view} DO INSTEAD
+ INSERT INTO {schematable} ({cols}) VALUES ({newcols}) RETURNING {return_cols};
+CREATE OR REPLACE RULE "_UPDATE" AS ON UPDATE TO {view} DO INSTEAD
+ UPDATE {schematable} SET {assign} WHERE {origpkey} = NEW.{origpkey};""".format(
+ view=self.view,
+ schematable=self.schematable,
+ cols=cols,
+ newcols=new_cols,
+ return_cols=return_cols,
+ assign=assign_cols,
+ origpkey=self.colOrigPkey,
+ )
def onOK(self):
# execute and commit the code
@@ -284,5 +326,7 @@ def onOK(self):
finally:
QApplication.restoreOverrideCursor()
- QMessageBox.information(self, "DB Manager", "Versioning was successfully created.")
+ QMessageBox.information(
+ self, "DB Manager", "Versioning was successfully created."
+ )
self.accept()
diff --git a/python/plugins/db_manager/db_plugins/postgis/sql_dictionary.py b/python/plugins/db_manager/db_plugins/postgis/sql_dictionary.py
index ea7c6de97a11..9b8f1f19a485 100644
--- a/python/plugins/db_manager/db_plugins/postgis/sql_dictionary.py
+++ b/python/plugins/db_manager/db_plugins/postgis/sql_dictionary.py
@@ -15,185 +15,855 @@
***************************************************************************
"""
-__author__ = 'Giuseppe Sucameli'
-__date__ = 'April 2012'
-__copyright__ = '(C) 2012, Giuseppe Sucameli'
+__author__ = "Giuseppe Sucameli"
+__date__ = "April 2012"
+__copyright__ = "(C) 2012, Giuseppe Sucameli"
# keywords
keywords = [
# TODO get them from a reference page
- "action", "add", "after", "all", "alter", "analyze", "and", "as", "asc",
- "before", "begin", "between", "by", "cascade", "case", "cast", "check",
- "collate", "column", "commit", "constraint", "create", "cross", "current_date",
- "current_time", "current_timestamp", "default", "deferrable", "deferred",
- "delete", "desc", "distinct", "drop", "each", "else", "end", "escape",
- "except", "exists", "for", "foreign", "from", "full", "group", "having",
- "ignore", "immediate", "in", "initially", "inner", "insert", "intersect",
- "into", "is", "isnull", "join", "key", "left", "like", "limit", "match",
- "natural", "no", "not", "notnull", "null", "of", "offset", "on", "or", "order",
- "outer", "primary", "references", "release", "restrict", "right", "rollback",
- "row", "savepoint", "select", "set", "table", "temporary", "then", "to",
- "transaction", "trigger", "union", "unique", "update", "using", "values",
- "view", "when", "where",
-
- "absolute", "admin", "aggregate", "alias", "allocate", "analyse", "any", "are",
- "array", "asensitive", "assertion", "asymmetric", "at", "atomic",
- "authorization", "avg", "bigint", "binary", "bit", "bit_length", "blob",
- "boolean", "both", "breadth", "call", "called", "cardinality", "cascaded",
- "catalog", "ceil", "ceiling", "char", "character", "character_length",
- "char_length", "class", "clob", "close", "coalesce", "collation", "collect",
- "completion", "condition", "connect", "connection", "constraints",
- "constructor", "continue", "convert", "corr", "corresponding", "count",
- "covar_pop", "covar_samp", "cube", "cume_dist", "current",
- "current_default_transform_group", "current_path", "current_role",
- "current_transform_group_for_type", "current_user", "cursor", "cycle", "data",
- "date", "day", "deallocate", "dec", "decimal", "declare", "dense_rank",
- "depth", "deref", "describe", "descriptor", "destroy", "destructor",
- "deterministic", "diagnostics", "dictionary", "disconnect", "do", "domain",
- "double", "dynamic", "element", "end-exec", "equals", "every", "exception",
- "exec", "execute", "exp", "external", "extract", "false", "fetch", "filter",
- "first", "float", "floor", "found", "free", "freeze", "function", "fusion",
- "general", "get", "global", "go", "goto", "grant", "grouping", "hold", "host",
- "hour", "identity", "ilike", "indicator", "initialize", "inout", "input",
- "insensitive", "int", "integer", "intersection", "interval", "isolation",
- "iterate", "language", "large", "last", "lateral", "leading", "less", "level",
- "ln", "local", "localtime", "localtimestamp", "locator", "lower", "map", "max",
- "member", "merge", "method", "min", "minute", "mod", "modifies", "modify",
- "module", "month", "multiset", "names", "national", "nchar", "nclob", "new",
- "next", "none", "normalize", "nullif", "numeric", "object", "octet_length",
- "off", "old", "only", "open", "operation", "option", "ordinality", "out",
- "output", "over", "overlaps", "overlay", "pad", "parameter", "parameters",
- "partial", "partition", "path", "percentile_cont", "percentile_disc",
- "percent_rank", "placing", "position", "postfix", "power", "precision",
- "prefix", "preorder", "prepare", "preserve", "prior", "privileges",
- "procedure", "public", "range", "rank", "read", "reads", "real", "recursive",
- "ref", "referencing", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept",
- "regr_r2", "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "relative",
- "result", "return", "returning", "returns", "revoke", "role", "rollup",
- "routine", "rows", "row_number", "schema", "scope", "scroll", "search",
- "second", "section", "sensitive", "sequence", "session", "session_user",
- "sets", "similar", "size", "smallint", "some", "space", "specific",
- "specifictype", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate",
- "sqlwarning", "sqrt", "start", "state", "statement", "static", "stddev_pop",
- "stddev_samp", "structure", "submultiset", "substring", "sum", "symmetric",
- "system", "system_user", "tablesample", "terminate", "than", "time",
- "timestamp", "timezone_hour", "timezone_minute", "trailing", "translate",
- "translation", "treat", "trim", "true", "uescape", "under", "unknown",
- "unnest", "upper", "usage", "user", "value", "varchar", "variable", "varying",
- "var_pop", "var_samp", "verbose", "whenever", "width_bucket", "window", "with",
- "within", "without", "work", "write", "xml", "xmlagg", "xmlattributes",
- "xmlbinary", "xmlcomment", "xmlconcat", "xmlelement", "xmlforest",
- "xmlnamespaces", "xmlparse", "xmlpi", "xmlroot", "xmlserialize", "year", "zone"
+ "action",
+ "add",
+ "after",
+ "all",
+ "alter",
+ "analyze",
+ "and",
+ "as",
+ "asc",
+ "before",
+ "begin",
+ "between",
+ "by",
+ "cascade",
+ "case",
+ "cast",
+ "check",
+ "collate",
+ "column",
+ "commit",
+ "constraint",
+ "create",
+ "cross",
+ "current_date",
+ "current_time",
+ "current_timestamp",
+ "default",
+ "deferrable",
+ "deferred",
+ "delete",
+ "desc",
+ "distinct",
+ "drop",
+ "each",
+ "else",
+ "end",
+ "escape",
+ "except",
+ "exists",
+ "for",
+ "foreign",
+ "from",
+ "full",
+ "group",
+ "having",
+ "ignore",
+ "immediate",
+ "in",
+ "initially",
+ "inner",
+ "insert",
+ "intersect",
+ "into",
+ "is",
+ "isnull",
+ "join",
+ "key",
+ "left",
+ "like",
+ "limit",
+ "match",
+ "natural",
+ "no",
+ "not",
+ "notnull",
+ "null",
+ "of",
+ "offset",
+ "on",
+ "or",
+ "order",
+ "outer",
+ "primary",
+ "references",
+ "release",
+ "restrict",
+ "right",
+ "rollback",
+ "row",
+ "savepoint",
+ "select",
+ "set",
+ "table",
+ "temporary",
+ "then",
+ "to",
+ "transaction",
+ "trigger",
+ "union",
+ "unique",
+ "update",
+ "using",
+ "values",
+ "view",
+ "when",
+ "where",
+ "absolute",
+ "admin",
+ "aggregate",
+ "alias",
+ "allocate",
+ "analyse",
+ "any",
+ "are",
+ "array",
+ "asensitive",
+ "assertion",
+ "asymmetric",
+ "at",
+ "atomic",
+ "authorization",
+ "avg",
+ "bigint",
+ "binary",
+ "bit",
+ "bit_length",
+ "blob",
+ "boolean",
+ "both",
+ "breadth",
+ "call",
+ "called",
+ "cardinality",
+ "cascaded",
+ "catalog",
+ "ceil",
+ "ceiling",
+ "char",
+ "character",
+ "character_length",
+ "char_length",
+ "class",
+ "clob",
+ "close",
+ "coalesce",
+ "collation",
+ "collect",
+ "completion",
+ "condition",
+ "connect",
+ "connection",
+ "constraints",
+ "constructor",
+ "continue",
+ "convert",
+ "corr",
+ "corresponding",
+ "count",
+ "covar_pop",
+ "covar_samp",
+ "cube",
+ "cume_dist",
+ "current",
+ "current_default_transform_group",
+ "current_path",
+ "current_role",
+ "current_transform_group_for_type",
+ "current_user",
+ "cursor",
+ "cycle",
+ "data",
+ "date",
+ "day",
+ "deallocate",
+ "dec",
+ "decimal",
+ "declare",
+ "dense_rank",
+ "depth",
+ "deref",
+ "describe",
+ "descriptor",
+ "destroy",
+ "destructor",
+ "deterministic",
+ "diagnostics",
+ "dictionary",
+ "disconnect",
+ "do",
+ "domain",
+ "double",
+ "dynamic",
+ "element",
+ "end-exec",
+ "equals",
+ "every",
+ "exception",
+ "exec",
+ "execute",
+ "exp",
+ "external",
+ "extract",
+ "false",
+ "fetch",
+ "filter",
+ "first",
+ "float",
+ "floor",
+ "found",
+ "free",
+ "freeze",
+ "function",
+ "fusion",
+ "general",
+ "get",
+ "global",
+ "go",
+ "goto",
+ "grant",
+ "grouping",
+ "hold",
+ "host",
+ "hour",
+ "identity",
+ "ilike",
+ "indicator",
+ "initialize",
+ "inout",
+ "input",
+ "insensitive",
+ "int",
+ "integer",
+ "intersection",
+ "interval",
+ "isolation",
+ "iterate",
+ "language",
+ "large",
+ "last",
+ "lateral",
+ "leading",
+ "less",
+ "level",
+ "ln",
+ "local",
+ "localtime",
+ "localtimestamp",
+ "locator",
+ "lower",
+ "map",
+ "max",
+ "member",
+ "merge",
+ "method",
+ "min",
+ "minute",
+ "mod",
+ "modifies",
+ "modify",
+ "module",
+ "month",
+ "multiset",
+ "names",
+ "national",
+ "nchar",
+ "nclob",
+ "new",
+ "next",
+ "none",
+ "normalize",
+ "nullif",
+ "numeric",
+ "object",
+ "octet_length",
+ "off",
+ "old",
+ "only",
+ "open",
+ "operation",
+ "option",
+ "ordinality",
+ "out",
+ "output",
+ "over",
+ "overlaps",
+ "overlay",
+ "pad",
+ "parameter",
+ "parameters",
+ "partial",
+ "partition",
+ "path",
+ "percentile_cont",
+ "percentile_disc",
+ "percent_rank",
+ "placing",
+ "position",
+ "postfix",
+ "power",
+ "precision",
+ "prefix",
+ "preorder",
+ "prepare",
+ "preserve",
+ "prior",
+ "privileges",
+ "procedure",
+ "public",
+ "range",
+ "rank",
+ "read",
+ "reads",
+ "real",
+ "recursive",
+ "ref",
+ "referencing",
+ "regr_avgx",
+ "regr_avgy",
+ "regr_count",
+ "regr_intercept",
+ "regr_r2",
+ "regr_slope",
+ "regr_sxx",
+ "regr_sxy",
+ "regr_syy",
+ "relative",
+ "result",
+ "return",
+ "returning",
+ "returns",
+ "revoke",
+ "role",
+ "rollup",
+ "routine",
+ "rows",
+ "row_number",
+ "schema",
+ "scope",
+ "scroll",
+ "search",
+ "second",
+ "section",
+ "sensitive",
+ "sequence",
+ "session",
+ "session_user",
+ "sets",
+ "similar",
+ "size",
+ "smallint",
+ "some",
+ "space",
+ "specific",
+ "specifictype",
+ "sql",
+ "sqlcode",
+ "sqlerror",
+ "sqlexception",
+ "sqlstate",
+ "sqlwarning",
+ "sqrt",
+ "start",
+ "state",
+ "statement",
+ "static",
+ "stddev_pop",
+ "stddev_samp",
+ "structure",
+ "submultiset",
+ "substring",
+ "sum",
+ "symmetric",
+ "system",
+ "system_user",
+ "tablesample",
+ "terminate",
+ "than",
+ "time",
+ "timestamp",
+ "timezone_hour",
+ "timezone_minute",
+ "trailing",
+ "translate",
+ "translation",
+ "treat",
+ "trim",
+ "true",
+ "uescape",
+ "under",
+ "unknown",
+ "unnest",
+ "upper",
+ "usage",
+ "user",
+ "value",
+ "varchar",
+ "variable",
+ "varying",
+ "var_pop",
+ "var_samp",
+ "verbose",
+ "whenever",
+ "width_bucket",
+ "window",
+ "with",
+ "within",
+ "without",
+ "work",
+ "write",
+ "xml",
+ "xmlagg",
+ "xmlattributes",
+ "xmlbinary",
+ "xmlcomment",
+ "xmlconcat",
+ "xmlelement",
+ "xmlforest",
+ "xmlnamespaces",
+ "xmlparse",
+ "xmlpi",
+ "xmlroot",
+ "xmlserialize",
+ "year",
+ "zone",
]
postgis_keywords = []
# functions
-functions = [
- "coalesce",
- "nullif", "quote", "random",
- "replace", "soundex"
-]
+functions = ["coalesce", "nullif", "quote", "random", "replace", "soundex"]
operators = [
- ' AND ', ' OR ', '||', ' < ', ' <= ', ' > ', ' >= ', ' = ', ' <> ', ' IS ', ' IS NOT ', ' IN ', ' LIKE ', ' GLOB ', ' MATCH ', ' REGEXP '
+ " AND ",
+ " OR ",
+ "||",
+ " < ",
+ " <= ",
+ " > ",
+ " >= ",
+ " = ",
+ " <> ",
+ " IS ",
+ " IS NOT ",
+ " IN ",
+ " LIKE ",
+ " GLOB ",
+ " MATCH ",
+ " REGEXP ",
]
math_functions = [
# SQL math functions
- "Abs", "ACos", "ASin", "ATan", "Cos", "Cot", "Degrees", "Exp", "Floor", "Log", "Log2",
- "Log10", "Pi", "Radians", "Round", "Sign", "Sin", "Sqrt", "StdDev_Pop", "StdDev_Samp", "Tan",
- "Var_Pop", "Var_Samp"]
+ "Abs",
+ "ACos",
+ "ASin",
+ "ATan",
+ "Cos",
+ "Cot",
+ "Degrees",
+ "Exp",
+ "Floor",
+ "Log",
+ "Log2",
+ "Log10",
+ "Pi",
+ "Radians",
+ "Round",
+ "Sign",
+ "Sin",
+ "Sqrt",
+ "StdDev_Pop",
+ "StdDev_Samp",
+ "Tan",
+ "Var_Pop",
+ "Var_Samp",
+]
-string_functions = ["Length", "Lower", "Upper", "Like", "Trim", "LTrim", "RTrim", "Replace", "Substr"]
+string_functions = [
+ "Length",
+ "Lower",
+ "Upper",
+ "Like",
+ "Trim",
+ "LTrim",
+ "RTrim",
+ "Replace",
+ "Substr",
+]
aggregate_functions = [
- "Max", "Min", "Avg", "Count", "Sum", "Group_Concat", "Total", "Var_Pop", "Var_Samp", "StdDev_Pop", "StdDev_Samp"
+ "Max",
+ "Min",
+ "Avg",
+ "Count",
+ "Sum",
+ "Group_Concat",
+ "Total",
+ "Var_Pop",
+ "Var_Samp",
+ "StdDev_Pop",
+ "StdDev_Samp",
]
postgis_functions = [ # from http://www.postgis.org/docs/reference.html
- # 7.1. PostgreSQL PostGIS Types
- "*box2d", "*box3d", "*box3d_extent", "*geometry", "*geometry_dump", "*geography",
- # 7.2. Management Functions
- "*addgeometrycolumn", "*dropgeometrycolumn", "*dropgeometrytable", "*postgis_full_version",
- "*postgis_geos_version", "*postgis_libxml_version", "*postgis_lib_build_date",
- "*postgis_lib_version", "*postgis_proj_version", "*postgis_scripts_build_date",
- "*postgis_scripts_installed", "*postgis_scripts_released", "*postgis_uses_stats", "*postgis_version",
- "*populate_geometry_columns", "*probe_geometry_columns", "*updategeometrysrid",
- # 7.3. Geometry Constructors
- "*ST_bdpolyfromtext", "*ST_bdmpolyfromtext", "*ST_geogfromtext", "*ST_geographyfromtext",
- "*ST_geogfromwkb", "*ST_geomcollfromtext", "*ST_geomfromewkb", "*ST_geomfromewkt",
- "*ST_geometryfromtext", "*ST_geomfromgml", "*ST_geomfromkml", "*ST_gmltosql", "*ST_geomfromtext",
- "*ST_geomfromwkb", "*ST_linefrommultipoint", "*ST_linefromtext", "*ST_linefromwkb",
- "*ST_linestringfromwkb", "*ST_makebox2d", "*ST_makebox3d", "ST_MakeLine", "*ST_makeenvelope",
- "ST_MakePolygon", "ST_MakePoint", "ST_MakePointM", "*ST_MLinefromtext", "*ST_mpointfromtext",
- "*ST_mpolyfromtext", "ST_Point", "*ST_pointfromtext", "*ST_pointfromwkb", "ST_Polygon",
- "*ST_polygonfromtext", "*ST_wkbtosql", "*ST_wkttosql",
- # 7.4. Geometry Accessors
- "GeometryType", "ST_Boundary", "*ST_coorddim", "ST_Dimension", "ST_EndPoint", "ST_Envelope",
- "ST_ExteriorRing", "ST_GeometryN", "ST_GeometryType", "ST_InteriorRingN", "ST_isClosed",
- "ST_isEmpty", "ST_isRing", "ST_isSimple", "ST_isValid", "ST_isValidReason", "ST_M", "ST_NDims",
- "ST_NPoints", "ST_NRings", "ST_NumGeometries", "ST_NumInteriorrings", "ST_NumInteriorring",
- "ST_NumPoints", "ST_PointN", "ST_Srid", "ST_StartPoint", "ST_Summary", "ST_X", "ST_Y", "ST_Z",
- "*ST_zmflag",
- # 7.5. Geometry Editors
- "ST_AddPoint", "ST_Affine", "ST_Force2D", "*ST_Force3D", "*ST_Force3dZ", "*ST_Force3DM",
- "*ST_Force_4d", "*ST_force_collection", "*ST_forcerhr", "*ST_linemerge", "*ST_collectionextract",
- "ST_Multi", "*ST_removepoint", "*ST_reverse", "*ST_rotate", "*ST_rotatex", "*ST_rotatey",
- "*ST_rotatez", "*ST_scale", "*ST_segmentize", "*ST_setpoint", "ST_SetSrid", "ST_SnapToGrid",
- "ST_Transform", "ST_Translate", "*ST_transscale",
- # 7.6. Geometry Outputs
- "*ST_asbinary", "*ST_asewkb", "*ST_asewkt", "*ST_asgeojson", "*ST_asgml", "*ST_ashexewkb", "*ST_askml",
- "*ST_assvg", "*ST_geohash", "ST_Astext",
- # 7.7. Operators
- # 7.8. Spatial Relationships and Measurements
- "ST_Area", "ST_Azimuth", "ST_Centroid", "ST_ClosestPoint", "ST_Contains", "ST_ContainsProperly",
- "ST_Covers", "ST_CoveredBy", "ST_Crosses", "*ST_linecrossingdirection", "ST_Cisjoint",
- "ST_Distance", "*ST_hausdorffdistance", "*ST_maxdistance", "ST_Distance_Sphere",
- "ST_Distance_Spheroid", "*ST_DFullyWithin", "ST_DWithin", "ST_Equals", "*ST_hasarc",
- "ST_Intersects", "ST_Length", "*ST_Length2d", "*ST_length3d", "ST_Length_Spheroid",
- "*ST_length2d_spheroid", "*ST_length3d_spheroid", "*ST_longestline", "*ST_orderingequals",
- "ST_Overlaps", "*ST_perimeter", "*ST_perimeter2d", "*ST_perimeter3d", "ST_PointOnSurface",
- "ST_Relate", "ST_ShortestLine", "ST_Touches", "ST_Within",
- # 7.9. Geometry Processing Functions
- "ST_Buffer", "ST_BuildArea", "ST_Collect", "ST_ConvexHull", "*ST_curvetoline", "ST_Difference",
- "ST_Dump", "*ST_dumppoints", "*ST_dumprings", "ST_Intersection", "*ST_linetocurve", "*ST_memunion",
- "*ST_minimumboundingcircle", "*ST_polygonize", "*ST_shift_longitude", "ST_Simplify",
- "ST_SimplifyPreserveTopology", "ST_SymDifference", "ST_Union",
- # 7.10. Linear Referencing
- "ST_Line_Interpolate_Point", "ST_Line_Locate_Point", "ST_Line_Substring",
- "*ST_locate_along_measure", "*ST_locate_between_measures", "*ST_locatebetweenelevations",
- "*ST_addmeasure",
- # 7.11. Long Transactions Support
- "*addauth", "*checkauth", "*disablelongtransactions", "*enablelongtransactions", "*lockrow",
- "*unlockrows",
- # 7.12. Miscellaneous Functions
- "*ST_accum", "*box2d", "*box3d", "*ST_estimated_extent", "*ST_expand", "ST_Extent", "*ST_extent3d",
- "*find_srid", "*ST_mem_size", "*ST_point_inside_circle", "ST_XMax", "ST_XMin", "ST_YMax", "ST_YMin",
- "ST_ZMax", "ST_ZMin",
- # 7.13. Exceptional Functions
- "*postgis_addbbox", "*postgis_dropbbox", "*postgis_hasbbox",
- # Raster functions
- "AddRasterConstraints", "DropRasterConstraints", "AddOverviewConstraints", "DropOverviewConstraints",
- "PostGIS_GDAL_Version", "PostGIS_Raster_Lib_Build_Date", "PostGIS_Raster_Lib_Version", "ST_GDALDrivers",
- "UpdateRasterSRID", "ST_CreateOverview", "ST_AddBand", "ST_AsRaster", "ST_Band", "ST_MakeEmptyCoverage",
- "ST_MakeEmptyRaster", "ST_Tile", "ST_Retile", "ST_FromGDALRaster", "ST_GeoReference", "ST_Height",
- "ST_IsEmpty", "ST_MemSize", "ST_MetaData", "ST_NumBands", "ST_PixelHeight", "ST_PixelWidth", "ST_ScaleX",
- "ST_ScaleY", "ST_RasterToWorldCoord", "ST_RasterToWorldCoordX", "ST_RasterToWorldCoordY", "ST_Rotation",
- "ST_SkewX", "ST_SkewY", "ST_SRID", "ST_Summary", "ST_UpperLeftX", "ST_UpperLeftY", "ST_Width",
- "ST_WorldToRasterCoord", "ST_WorldToRasterCoordX", "ST_WorldToRasterCoordY", "ST_BandMetaData",
- "ST_BandNoDataValue", "ST_BandIsNoData", "ST_BandPath", "ST_BandFileSize", "ST_BandFileTimestamp",
- "ST_BandPixelType", "ST_MinPossibleValue", "ST_HasNoBand", "ST_PixelAsPolygon", "ST_PixelAsPolygons",
- "ST_PixelAsPoint", "ST_PixelAsPoints", "ST_PixelAsCentroid", "ST_PixelAsCentroids", "ST_Value",
- "ST_NearestValue", "ST_Neighborhood", "ST_SetValue", "ST_SetValues", "ST_DumpValues", "ST_PixelOfValue",
- "ST_SetGeoReference", "ST_SetRotation", "ST_SetScale", "ST_SetSkew", "ST_SetSRID", "ST_SetUpperLeft",
- "ST_Resample", "ST_Rescale", "ST_Reskew", "ST_SnapToGrid", "ST_Resize", "ST_Transform",
- "ST_SetBandNoDataValue", "ST_SetBandIsNoData", "ST_SetBandPath", "ST_SetBandIndex", "ST_Count",
- "ST_CountAgg", "ST_Histogram", "ST_Quantile", "ST_SummaryStats", "ST_SummaryStatsAgg", "ST_ValueCount",
- "ST_RastFromWKB", "ST_RastFromHexWKB", "ST_AsBinary", "ST_AsWKB", "ST_AsHexWKB", "ST_AsGDALRaster",
- "ST_AsJPEG", "ST_AsPNG", "ST_AsTIFF", "ST_Clip", "ST_ColorMap", "ST_Grayscale", "ST_Intersection",
- "ST_MapAlgebra", "ST_MapAlgebraExpr", "ST_MapAlgebraFct", "ST_MapAlgebraFctNgb", "ST_Reclass", "ST_Union",
- "ST_Distinct4ma", "ST_InvDistWeight4ma", "ST_Max4ma", "ST_Mean4ma", "ST_Min4ma", "ST_MinDist4ma",
- "ST_Range4ma", "ST_StdDev4ma", "ST_Sum4ma", "ST_Aspect", "ST_HillShade", "ST_Roughness", "ST_Slope",
- "ST_TPI", "ST_TRI",
+ # 7.1. PostgreSQL PostGIS Types
+ "*box2d",
+ "*box3d",
+ "*box3d_extent",
+ "*geometry",
+ "*geometry_dump",
+ "*geography",
+ # 7.2. Management Functions
+ "*addgeometrycolumn",
+ "*dropgeometrycolumn",
+ "*dropgeometrytable",
+ "*postgis_full_version",
+ "*postgis_geos_version",
+ "*postgis_libxml_version",
+ "*postgis_lib_build_date",
+ "*postgis_lib_version",
+ "*postgis_proj_version",
+ "*postgis_scripts_build_date",
+ "*postgis_scripts_installed",
+ "*postgis_scripts_released",
+ "*postgis_uses_stats",
+ "*postgis_version",
+ "*populate_geometry_columns",
+ "*probe_geometry_columns",
+ "*updategeometrysrid",
+ # 7.3. Geometry Constructors
+ "*ST_bdpolyfromtext",
+ "*ST_bdmpolyfromtext",
+ "*ST_geogfromtext",
+ "*ST_geographyfromtext",
+ "*ST_geogfromwkb",
+ "*ST_geomcollfromtext",
+ "*ST_geomfromewkb",
+ "*ST_geomfromewkt",
+ "*ST_geometryfromtext",
+ "*ST_geomfromgml",
+ "*ST_geomfromkml",
+ "*ST_gmltosql",
+ "*ST_geomfromtext",
+ "*ST_geomfromwkb",
+ "*ST_linefrommultipoint",
+ "*ST_linefromtext",
+ "*ST_linefromwkb",
+ "*ST_linestringfromwkb",
+ "*ST_makebox2d",
+ "*ST_makebox3d",
+ "ST_MakeLine",
+ "*ST_makeenvelope",
+ "ST_MakePolygon",
+ "ST_MakePoint",
+ "ST_MakePointM",
+ "*ST_MLinefromtext",
+ "*ST_mpointfromtext",
+ "*ST_mpolyfromtext",
+ "ST_Point",
+ "*ST_pointfromtext",
+ "*ST_pointfromwkb",
+ "ST_Polygon",
+ "*ST_polygonfromtext",
+ "*ST_wkbtosql",
+ "*ST_wkttosql",
+ # 7.4. Geometry Accessors
+ "GeometryType",
+ "ST_Boundary",
+ "*ST_coorddim",
+ "ST_Dimension",
+ "ST_EndPoint",
+ "ST_Envelope",
+ "ST_ExteriorRing",
+ "ST_GeometryN",
+ "ST_GeometryType",
+ "ST_InteriorRingN",
+ "ST_isClosed",
+ "ST_isEmpty",
+ "ST_isRing",
+ "ST_isSimple",
+ "ST_isValid",
+ "ST_isValidReason",
+ "ST_M",
+ "ST_NDims",
+ "ST_NPoints",
+ "ST_NRings",
+ "ST_NumGeometries",
+ "ST_NumInteriorrings",
+ "ST_NumInteriorring",
+ "ST_NumPoints",
+ "ST_PointN",
+ "ST_Srid",
+ "ST_StartPoint",
+ "ST_Summary",
+ "ST_X",
+ "ST_Y",
+ "ST_Z",
+ "*ST_zmflag",
+ # 7.5. Geometry Editors
+ "ST_AddPoint",
+ "ST_Affine",
+ "ST_Force2D",
+ "*ST_Force3D",
+ "*ST_Force3dZ",
+ "*ST_Force3DM",
+ "*ST_Force_4d",
+ "*ST_force_collection",
+ "*ST_forcerhr",
+ "*ST_linemerge",
+ "*ST_collectionextract",
+ "ST_Multi",
+ "*ST_removepoint",
+ "*ST_reverse",
+ "*ST_rotate",
+ "*ST_rotatex",
+ "*ST_rotatey",
+ "*ST_rotatez",
+ "*ST_scale",
+ "*ST_segmentize",
+ "*ST_setpoint",
+ "ST_SetSrid",
+ "ST_SnapToGrid",
+ "ST_Transform",
+ "ST_Translate",
+ "*ST_transscale",
+ # 7.6. Geometry Outputs
+ "*ST_asbinary",
+ "*ST_asewkb",
+ "*ST_asewkt",
+ "*ST_asgeojson",
+ "*ST_asgml",
+ "*ST_ashexewkb",
+ "*ST_askml",
+ "*ST_assvg",
+ "*ST_geohash",
+ "ST_Astext",
+ # 7.7. Operators
+ # 7.8. Spatial Relationships and Measurements
+ "ST_Area",
+ "ST_Azimuth",
+ "ST_Centroid",
+ "ST_ClosestPoint",
+ "ST_Contains",
+ "ST_ContainsProperly",
+ "ST_Covers",
+ "ST_CoveredBy",
+ "ST_Crosses",
+ "*ST_linecrossingdirection",
+ "ST_Cisjoint",
+ "ST_Distance",
+ "*ST_hausdorffdistance",
+ "*ST_maxdistance",
+ "ST_Distance_Sphere",
+ "ST_Distance_Spheroid",
+ "*ST_DFullyWithin",
+ "ST_DWithin",
+ "ST_Equals",
+ "*ST_hasarc",
+ "ST_Intersects",
+ "ST_Length",
+ "*ST_Length2d",
+ "*ST_length3d",
+ "ST_Length_Spheroid",
+ "*ST_length2d_spheroid",
+ "*ST_length3d_spheroid",
+ "*ST_longestline",
+ "*ST_orderingequals",
+ "ST_Overlaps",
+ "*ST_perimeter",
+ "*ST_perimeter2d",
+ "*ST_perimeter3d",
+ "ST_PointOnSurface",
+ "ST_Relate",
+ "ST_ShortestLine",
+ "ST_Touches",
+ "ST_Within",
+ # 7.9. Geometry Processing Functions
+ "ST_Buffer",
+ "ST_BuildArea",
+ "ST_Collect",
+ "ST_ConvexHull",
+ "*ST_curvetoline",
+ "ST_Difference",
+ "ST_Dump",
+ "*ST_dumppoints",
+ "*ST_dumprings",
+ "ST_Intersection",
+ "*ST_linetocurve",
+ "*ST_memunion",
+ "*ST_minimumboundingcircle",
+ "*ST_polygonize",
+ "*ST_shift_longitude",
+ "ST_Simplify",
+ "ST_SimplifyPreserveTopology",
+ "ST_SymDifference",
+ "ST_Union",
+ # 7.10. Linear Referencing
+ "ST_Line_Interpolate_Point",
+ "ST_Line_Locate_Point",
+ "ST_Line_Substring",
+ "*ST_locate_along_measure",
+ "*ST_locate_between_measures",
+ "*ST_locatebetweenelevations",
+ "*ST_addmeasure",
+ # 7.11. Long Transactions Support
+ "*addauth",
+ "*checkauth",
+ "*disablelongtransactions",
+ "*enablelongtransactions",
+ "*lockrow",
+ "*unlockrows",
+ # 7.12. Miscellaneous Functions
+ "*ST_accum",
+ "*box2d",
+ "*box3d",
+ "*ST_estimated_extent",
+ "*ST_expand",
+ "ST_Extent",
+ "*ST_extent3d",
+ "*find_srid",
+ "*ST_mem_size",
+ "*ST_point_inside_circle",
+ "ST_XMax",
+ "ST_XMin",
+ "ST_YMax",
+ "ST_YMin",
+ "ST_ZMax",
+ "ST_ZMin",
+ # 7.13. Exceptional Functions
+ "*postgis_addbbox",
+ "*postgis_dropbbox",
+ "*postgis_hasbbox",
+ # Raster functions
+ "AddRasterConstraints",
+ "DropRasterConstraints",
+ "AddOverviewConstraints",
+ "DropOverviewConstraints",
+ "PostGIS_GDAL_Version",
+ "PostGIS_Raster_Lib_Build_Date",
+ "PostGIS_Raster_Lib_Version",
+ "ST_GDALDrivers",
+ "UpdateRasterSRID",
+ "ST_CreateOverview",
+ "ST_AddBand",
+ "ST_AsRaster",
+ "ST_Band",
+ "ST_MakeEmptyCoverage",
+ "ST_MakeEmptyRaster",
+ "ST_Tile",
+ "ST_Retile",
+ "ST_FromGDALRaster",
+ "ST_GeoReference",
+ "ST_Height",
+ "ST_IsEmpty",
+ "ST_MemSize",
+ "ST_MetaData",
+ "ST_NumBands",
+ "ST_PixelHeight",
+ "ST_PixelWidth",
+ "ST_ScaleX",
+ "ST_ScaleY",
+ "ST_RasterToWorldCoord",
+ "ST_RasterToWorldCoordX",
+ "ST_RasterToWorldCoordY",
+ "ST_Rotation",
+ "ST_SkewX",
+ "ST_SkewY",
+ "ST_SRID",
+ "ST_Summary",
+ "ST_UpperLeftX",
+ "ST_UpperLeftY",
+ "ST_Width",
+ "ST_WorldToRasterCoord",
+ "ST_WorldToRasterCoordX",
+ "ST_WorldToRasterCoordY",
+ "ST_BandMetaData",
+ "ST_BandNoDataValue",
+ "ST_BandIsNoData",
+ "ST_BandPath",
+ "ST_BandFileSize",
+ "ST_BandFileTimestamp",
+ "ST_BandPixelType",
+ "ST_MinPossibleValue",
+ "ST_HasNoBand",
+ "ST_PixelAsPolygon",
+ "ST_PixelAsPolygons",
+ "ST_PixelAsPoint",
+ "ST_PixelAsPoints",
+ "ST_PixelAsCentroid",
+ "ST_PixelAsCentroids",
+ "ST_Value",
+ "ST_NearestValue",
+ "ST_Neighborhood",
+ "ST_SetValue",
+ "ST_SetValues",
+ "ST_DumpValues",
+ "ST_PixelOfValue",
+ "ST_SetGeoReference",
+ "ST_SetRotation",
+ "ST_SetScale",
+ "ST_SetSkew",
+ "ST_SetSRID",
+ "ST_SetUpperLeft",
+ "ST_Resample",
+ "ST_Rescale",
+ "ST_Reskew",
+ "ST_SnapToGrid",
+ "ST_Resize",
+ "ST_Transform",
+ "ST_SetBandNoDataValue",
+ "ST_SetBandIsNoData",
+ "ST_SetBandPath",
+ "ST_SetBandIndex",
+ "ST_Count",
+ "ST_CountAgg",
+ "ST_Histogram",
+ "ST_Quantile",
+ "ST_SummaryStats",
+ "ST_SummaryStatsAgg",
+ "ST_ValueCount",
+ "ST_RastFromWKB",
+ "ST_RastFromHexWKB",
+ "ST_AsBinary",
+ "ST_AsWKB",
+ "ST_AsHexWKB",
+ "ST_AsGDALRaster",
+ "ST_AsJPEG",
+ "ST_AsPNG",
+ "ST_AsTIFF",
+ "ST_Clip",
+ "ST_ColorMap",
+ "ST_Grayscale",
+ "ST_Intersection",
+ "ST_MapAlgebra",
+ "ST_MapAlgebraExpr",
+ "ST_MapAlgebraFct",
+ "ST_MapAlgebraFctNgb",
+ "ST_Reclass",
+ "ST_Union",
+ "ST_Distinct4ma",
+ "ST_InvDistWeight4ma",
+ "ST_Max4ma",
+ "ST_Mean4ma",
+ "ST_Min4ma",
+ "ST_MinDist4ma",
+ "ST_Range4ma",
+ "ST_StdDev4ma",
+ "ST_Sum4ma",
+ "ST_Aspect",
+ "ST_HillShade",
+ "ST_Roughness",
+ "ST_Slope",
+ "ST_TPI",
+ "ST_TRI",
]
# constants
@@ -203,7 +873,7 @@
def getSqlDictionary(spatial=True):
def strip_star(s):
- if s[0] == '*':
+ if s[0] == "*":
return s.lower()[1:]
else:
return s.lower()
@@ -215,13 +885,17 @@ def strip_star(s):
f += postgis_functions
c += postgis_constants
- return {'keyword': list(map(strip_star, k)), 'constant': list(map(strip_star, c)), 'function': list(map(strip_star, f))}
+ return {
+ "keyword": list(map(strip_star, k)),
+ "constant": list(map(strip_star, c)),
+ "function": list(map(strip_star, f)),
+ }
def getQueryBuilderDictionary():
# concat functions
def ff(l):
- return [s for s in l if s[0] != '*']
+ return [s for s in l if s[0] != "*"]
def add_paren(l):
return [s + "(" for s in l]
@@ -231,4 +905,4 @@ def add_paren(l):
agg = sorted(add_paren(ff(aggregate_functions)))
op = ff(operators)
s = sorted(add_paren(ff(string_functions)))
- return {'function': foo, 'math': m, 'aggregate': agg, 'operator': op, 'string': s}
+ return {"function": foo, "math": m, "aggregate": agg, "operator": op, "string": s}
diff --git a/python/plugins/db_manager/db_plugins/spatialite/connector.py b/python/plugins/db_manager/db_plugins/spatialite/connector.py
index 3956f52e8015..ca1b9732fe48 100644
--- a/python/plugins/db_manager/db_plugins/spatialite/connector.py
+++ b/python/plugins/db_manager/db_plugins/spatialite/connector.py
@@ -42,7 +42,11 @@ def __init__(self, uri):
self.dbname = uri.database()
if not QFile.exists(self.dbname):
- raise ConnectionError(QApplication.translate("DBManagerPlugin", '"{0}" not found').format(self.dbname))
+ raise ConnectionError(
+ QApplication.translate("DBManagerPlugin", '"{0}" not found').format(
+ self.dbname
+ )
+ )
try:
self.connection = spatialite_connect(self._connectionInfo())
@@ -85,12 +89,12 @@ def isValidDatabase(self, path):
return isValid
def _checkSpatial(self):
- """ check if it's a valid SpatiaLite db """
+ """check if it's a valid SpatiaLite db"""
self.has_spatial = self._checkGeometryColumnsTable()
return self.has_spatial
def _checkRaster(self):
- """ check if it's a rasterite db """
+ """check if it's a rasterite db"""
self.has_raster = self._checkRasterTables()
return self.has_raster
@@ -121,17 +125,19 @@ def getInfo(self):
return c.fetchone()
def getSpatialInfo(self):
- """ returns tuple about SpatiaLite support:
- - lib version
- - geos version
- - proj version
+ """returns tuple about SpatiaLite support:
+ - lib version
+ - geos version
+ - proj version
"""
if not self.has_spatial:
return
c = self._get_cursor()
try:
- self._execute(c, "SELECT spatialite_version(), geos_version(), proj4_version()")
+ self._execute(
+ c, "SELECT spatialite_version(), geos_version(), proj4_version()"
+ )
except DbError:
return
@@ -154,17 +160,26 @@ def hasCreateSpatialViewSupport(self):
def fieldTypes(self):
return [
- "integer", "bigint", "smallint", # integers
- "real", "double", "float", "numeric", # floats
- "varchar", "varchar(255)", "character(20)", "text", # strings
- "date", "datetime" # date/time
+ "integer",
+ "bigint",
+ "smallint", # integers
+ "real",
+ "double",
+ "float",
+ "numeric", # floats
+ "varchar",
+ "varchar(255)",
+ "character(20)",
+ "text", # strings
+ "date",
+ "datetime", # date/time
]
def getSchemas(self):
return None
def getTables(self, schema=None, add_sys_tables=False):
- """ get list of tables """
+ """get list of tables"""
tablenames = []
items = []
@@ -197,16 +212,16 @@ def getTables(self, schema=None, add_sys_tables=False):
sql = "SELECT f_table_name, f_geometry_column FROM geometry_columns WHERE spatial_index_enabled = 1"
self._execute(c, sql)
for idx_item in c.fetchall():
- sys_tables.append('idx_%s_%s' % idx_item)
- sys_tables.append('idx_%s_%s_node' % idx_item)
- sys_tables.append('idx_%s_%s_parent' % idx_item)
- sys_tables.append('idx_%s_%s_rowid' % idx_item)
+ sys_tables.append("idx_%s_%s" % idx_item)
+ sys_tables.append("idx_%s_%s_node" % idx_item)
+ sys_tables.append("idx_%s_%s_parent" % idx_item)
+ sys_tables.append("idx_%s_%s_rowid" % idx_item)
sql = "SELECT name, type = 'view' FROM sqlite_master WHERE type IN ('table', 'view')"
self._execute(c, sql)
for tbl in c.fetchall():
- if tablenames.count(tbl[0]) <= 0 and not tbl[0].startswith('idx_'):
+ if tablenames.count(tbl[0]) <= 0 and not tbl[0].startswith("idx_"):
if not add_sys_tables and tbl[0] in sys_tables:
continue
item = list(tbl)
@@ -219,16 +234,16 @@ def getTables(self, schema=None, add_sys_tables=False):
return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
def getVectorTables(self, schema=None):
- """ get list of table with a geometry column
- it returns:
- name (table name)
- type = 'view' (is a view?)
- geometry_column:
- f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
- f_geometry_column
- type
- coord_dimension
- srid
+ """get list of table with a geometry column
+ it returns:
+ name (table name)
+ type = 'view' (is a view?)
+ geometry_column:
+ f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
+ f_geometry_column
+ type
+ coord_dimension
+ srid
"""
if self.has_geometry_columns:
@@ -253,10 +268,13 @@ def getVectorTables(self, schema=None):
cols = "g.type,g.coord_dimension"
# get geometry info from geometry_columns if exists
- sql = """SELECT m.name, m.type = 'view', g.f_table_name, g.f_geometry_column, %s, g.srid
+ sql = (
+ """SELECT m.name, m.type = 'view', g.f_table_name, g.f_geometry_column, %s, g.srid
FROM sqlite_master AS m JOIN geometry_columns AS g ON upper(m.name) = upper(g.f_table_name)
WHERE m.type in ('table', 'view')
- ORDER BY m.name, g.f_geometry_column""" % cols
+ ORDER BY m.name, g.f_geometry_column"""
+ % cols
+ )
else:
return []
@@ -273,14 +291,14 @@ def getVectorTables(self, schema=None):
return items
def getRasterTables(self, schema=None):
- """ get list of table with a geometry column
- it returns:
- name (table name)
- type = 'view' (is a view?)
- geometry_column:
- r.table_name (the prefix table name, use this to load the layer)
- r.geometry_column
- srid
+ """get list of table with a geometry column
+ it returns:
+ name (table name)
+ type = 'view' (is a view?)
+ geometry_column:
+ r.table_name (the prefix table name, use this to load the layer)
+ r.geometry_column
+ srid
"""
if not self.has_geometry_columns:
@@ -314,14 +332,14 @@ def getTableRowCount(self, table):
return ret[0] if ret is not None else None
def getTableFields(self, table):
- """ return list of columns in table """
+ """return list of columns in table"""
c = self._get_cursor()
sql = "PRAGMA table_info(%s)" % (self.quoteId(table))
self._execute(c, sql)
return c.fetchall()
def getTableIndexes(self, table):
- """ get info about table's indexes """
+ """get info about table's indexes"""
c = self._get_cursor()
sql = "PRAGMA index_list(%s)" % (self.quoteId(table))
self._execute(c, sql)
@@ -340,10 +358,7 @@ def getTableIndexes(self, table):
self._execute(c, sql)
idx = [num, name, unique]
- cols = [
- cid
- for seq, cid, cname in c.fetchall()
- ]
+ cols = [cid for seq, cid, cname in c.fetchall()]
idx.append(cols)
indexes[i] = idx
@@ -355,41 +370,50 @@ def getTableConstraints(self, table):
def getTableTriggers(self, table):
c = self._get_cursor()
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT name, sql FROM sqlite_master WHERE tbl_name = %s AND type = 'trigger'" % (
- self.quoteString(tablename))
+ sql = (
+ "SELECT name, sql FROM sqlite_master WHERE tbl_name = %s AND type = 'trigger'"
+ % (self.quoteString(tablename))
+ )
self._execute(c, sql)
return c.fetchall()
def deleteTableTrigger(self, trigger, table=None):
- """Deletes trigger """
+ """Deletes trigger"""
sql = "DROP TRIGGER %s" % self.quoteId(trigger)
self._execute_and_commit(sql)
def getTableExtent(self, table, geom):
- """ find out table extent """
+ """find out table extent"""
schema, tablename = self.getSchemaTableName(table)
c = self._get_cursor()
if self.isRasterTable(table):
- tablename = tablename.replace('_rasters', '_metadata')
- geom = 'geometry'
+ tablename = tablename.replace("_rasters", "_metadata")
+ geom = "geometry"
- sql = """SELECT Min(MbrMinX(%(geom)s)), Min(MbrMinY(%(geom)s)), Max(MbrMaxX(%(geom)s)), Max(MbrMaxY(%(geom)s))
- FROM %(table)s """ % {'geom': self.quoteId(geom),
- 'table': self.quoteId(tablename)}
+ sql = """SELECT Min(MbrMinX({geom})), Min(MbrMinY({geom})), Max(MbrMaxX({geom})), Max(MbrMaxY({geom}))
+ FROM {table} """.format(
+ geom=self.quoteId(geom), table=self.quoteId(tablename)
+ )
self._execute(c, sql)
return c.fetchone()
def getViewDefinition(self, view):
- """ returns definition of the view """
+ """returns definition of the view"""
schema, tablename = self.getSchemaTableName(view)
- sql = "SELECT sql FROM sqlite_master WHERE type = 'view' AND name = %s" % self.quoteString(tablename)
+ sql = (
+ "SELECT sql FROM sqlite_master WHERE type = 'view' AND name = %s"
+ % self.quoteString(tablename)
+ )
c = self._execute(None, sql)
ret = c.fetchone()
return ret[0] if ret is not None else None
def getSpatialRefInfo(self, srid):
- sql = "SELECT ref_sys_name FROM spatial_ref_sys WHERE srid = %s" % self.quoteString(srid)
+ sql = (
+ "SELECT ref_sys_name FROM spatial_ref_sys WHERE srid = %s"
+ % self.quoteString(srid)
+ )
c = self._execute(None, sql)
ret = c.fetchone()
return ret[0] if ret is not None else None
@@ -397,8 +421,10 @@ def getSpatialRefInfo(self, srid):
def isVectorTable(self, table):
if self.has_geometry_columns:
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT count(*) FROM geometry_columns WHERE upper(f_table_name) = upper(%s)" % self.quoteString(
- tablename)
+ sql = (
+ "SELECT count(*) FROM geometry_columns WHERE upper(f_table_name) = upper(%s)"
+ % self.quoteString(tablename)
+ )
c = self._execute(None, sql)
ret = c.fetchone()
return ret is not None and ret[0] > 0
@@ -414,7 +440,8 @@ def isRasterTable(self, table):
FROM layer_params AS r JOIN geometry_columns AS g
ON upper(r.table_name||'_metadata') = upper(g.f_table_name)
WHERE upper(r.table_name) = upper(REPLACE(%s, '_rasters', ''))""" % self.quoteString(
- tablename)
+ tablename
+ )
c = self._execute(None, sql)
ret = c.fetchone()
return ret is not None and ret[0] > 0
@@ -423,8 +450,8 @@ def isRasterTable(self, table):
def createTable(self, table, field_defs, pkey):
"""Creates ordinary table
- 'fields' is array containing field definitions
- 'pkey' is the primary key name
+ 'fields' is array containing field definitions
+ 'pkey' is the primary key name
"""
if len(field_defs) == 0:
return False
@@ -439,7 +466,7 @@ def createTable(self, table, field_defs, pkey):
return True
def deleteTable(self, table):
- """Deletes table from the database """
+ """Deletes table from the database"""
if self.isRasterTable(table):
return False
@@ -447,14 +474,17 @@ def deleteTable(self, table):
sql = "DROP TABLE %s" % self.quoteId(table)
self._execute(c, sql)
schema, tablename = self.getSchemaTableName(table)
- sql = "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%s)" % self.quoteString(tablename)
+ sql = (
+ "DELETE FROM geometry_columns WHERE upper(f_table_name) = upper(%s)"
+ % self.quoteString(tablename)
+ )
self._execute(c, sql)
self._commit()
return True
def emptyTable(self, table):
- """Deletes all rows from table """
+ """Deletes all rows from table"""
if self.isRasterTable(table):
return False
@@ -462,7 +492,7 @@ def emptyTable(self, table):
self._execute_and_commit(sql)
def renameTable(self, table, new_table):
- """ rename a table """
+ """rename a table"""
schema, tablename = self.getSchemaTableName(table)
if new_table == tablename:
return
@@ -472,13 +502,16 @@ def renameTable(self, table, new_table):
c = self._get_cursor()
- sql = "ALTER TABLE %s RENAME TO %s" % (self.quoteId(table), self.quoteId(new_table))
+ sql = "ALTER TABLE {} RENAME TO {}".format(
+ self.quoteId(table), self.quoteId(new_table)
+ )
self._execute(c, sql)
# update geometry_columns
if self.has_geometry_columns:
- sql = "UPDATE geometry_columns SET f_table_name = %s WHERE upper(f_table_name) = upper(%s)" % (
- self.quoteString(new_table), self.quoteString(tablename))
+ sql = "UPDATE geometry_columns SET f_table_name = {} WHERE upper(f_table_name) = upper({})".format(
+ self.quoteString(new_table), self.quoteString(tablename)
+ )
self._execute(c, sql)
self._commit()
@@ -488,7 +521,7 @@ def moveTable(self, table, new_table, new_schema=None):
return self.renameTable(table, new_table)
def createView(self, view, query):
- sql = "CREATE VIEW %s AS %s" % (self.quoteId(view), query)
+ sql = f"CREATE VIEW {self.quoteId(view)} AS {query}"
self._execute_and_commit(sql)
def deleteView(self, view):
@@ -499,13 +532,16 @@ def deleteView(self, view):
# update geometry_columns
if self.has_geometry_columns:
- sql = "DELETE FROM geometry_columns WHERE f_table_name = %s" % self.quoteString(view)
+ sql = (
+ "DELETE FROM geometry_columns WHERE f_table_name = %s"
+ % self.quoteString(view)
+ )
self._execute(c, sql)
self._commit()
def renameView(self, view, new_name):
- """ rename view """
+ """rename view"""
return self.renameTable(view, new_name)
def createSpatialView(self, view, query):
@@ -516,50 +552,67 @@ def createSpatialView(self, view, query):
c = self._execute(None, sql)
geom_col = None
for r in c.fetchall():
- if r[2].upper() in ('POINT', 'LINESTRING', 'POLYGON',
- 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON'):
+ if r[2].upper() in (
+ "POINT",
+ "LINESTRING",
+ "POLYGON",
+ "MULTIPOINT",
+ "MULTILINESTRING",
+ "MULTIPOLYGON",
+ ):
geom_col = r[1]
break
if geom_col is None:
return
# get geometry type and srid
- sql = "SELECT geometrytype(%s), srid(%s) FROM %s LIMIT 1" % (self.quoteId(geom_col), self.quoteId(geom_col), self.quoteId(view))
+ sql = "SELECT geometrytype({}), srid({}) FROM {} LIMIT 1".format(
+ self.quoteId(geom_col), self.quoteId(geom_col), self.quoteId(view)
+ )
c = self._execute(None, sql)
r = c.fetchone()
if r is None:
return
gtype, gsrid = r
- gdim = 'XY'
- if ' ' in gtype:
- zm = gtype.split(' ')[1]
- gtype = gtype.split(' ')[0]
+ gdim = "XY"
+ if " " in gtype:
+ zm = gtype.split(" ")[1]
+ gtype = gtype.split(" ")[0]
gdim += zm
try:
- wkbType = ('POINT', 'LINESTRING', 'POLYGON', 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON').index(gtype) + 1
+ wkbType = (
+ "POINT",
+ "LINESTRING",
+ "POLYGON",
+ "MULTIPOINT",
+ "MULTILINESTRING",
+ "MULTIPOLYGON",
+ ).index(gtype) + 1
except:
wkbType = 0
- if 'Z' in gdim:
+ if "Z" in gdim:
wkbType += 1000
- if 'M' in gdim:
+ if "M" in gdim:
wkbType += 2000
sql = """INSERT INTO geometry_columns (f_table_name, f_geometry_column, geometry_type, coord_dimension, srid, spatial_index_enabled)
- VALUES (%s, %s, %s, %s, %s, 0)""" % (self.quoteId(view), self.quoteId(geom_col), wkbType, len(gdim), gsrid)
+ VALUES ({}, {}, {}, {}, {}, 0)""".format(
+ self.quoteId(view), self.quoteId(geom_col), wkbType, len(gdim), gsrid
+ )
self._execute_and_commit(sql)
def runVacuum(self):
- """ run vacuum on the db """
+ """run vacuum on the db"""
# Workaround http://bugs.python.org/issue28518
self.connection.isolation_level = None
c = self._get_cursor()
- c.execute('VACUUM')
- self.connection.isolation_level = '' # reset to default isolation
+ c.execute("VACUUM")
+ self.connection.isolation_level = "" # reset to default isolation
def addTableColumn(self, table, field_def):
- """Adds a column to table """
- sql = "ALTER TABLE %s ADD %s" % (self.quoteId(table), field_def)
+ """Adds a column to table"""
+ sql = f"ALTER TABLE {self.quoteId(table)} ADD {field_def}"
self._execute(None, sql)
sql = "SELECT InvalidateLayerStatistics(%s)" % (self.quoteId(table))
@@ -572,71 +625,93 @@ def addTableColumn(self, table, field_def):
return True
def deleteTableColumn(self, table, column):
- """Deletes column from a table """
+ """Deletes column from a table"""
if not self.isGeometryColumn(table, column):
return False # column editing not supported
# delete geometry column correctly
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT DiscardGeometryColumn(%s, %s)" % (self.quoteString(tablename), self.quoteString(column))
+ sql = "SELECT DiscardGeometryColumn({}, {})".format(
+ self.quoteString(tablename), self.quoteString(column)
+ )
self._execute_and_commit(sql)
- def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not_null=None, new_default=None, comment=None):
+ def updateTableColumn(
+ self,
+ table,
+ column,
+ new_name,
+ new_data_type=None,
+ new_not_null=None,
+ new_default=None,
+ comment=None,
+ ):
return False # column editing not supported
def renameTableColumn(self, table, column, new_name):
- """ rename column in a table """
+ """rename column in a table"""
return False # column editing not supported
def setColumnType(self, table, column, data_type):
- """Changes column type """
+ """Changes column type"""
return False # column editing not supported
def setColumnDefault(self, table, column, default):
- """Changes column's default value. If default=None drop default value """
+ """Changes column's default value. If default=None drop default value"""
return False # column editing not supported
def setColumnNull(self, table, column, is_null):
- """Changes whether column can contain null values """
+ """Changes whether column can contain null values"""
return False # column editing not supported
def isGeometryColumn(self, table, column):
c = self._get_cursor()
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT count(*) > 0 FROM geometry_columns WHERE upper(f_table_name) = upper(%s) AND upper(f_geometry_column) = upper(%s)" % (
- self.quoteString(tablename), self.quoteString(column))
+ sql = "SELECT count(*) > 0 FROM geometry_columns WHERE upper(f_table_name) = upper({}) AND upper(f_geometry_column) = upper({})".format(
+ self.quoteString(tablename), self.quoteString(column)
+ )
self._execute(c, sql)
- return c.fetchone()[0] == 't'
+ return c.fetchone()[0] == "t"
- def addGeometryColumn(self, table, geom_column='geometry', geom_type='POINT', srid=-1, dim=2):
+ def addGeometryColumn(
+ self, table, geom_column="geometry", geom_type="POINT", srid=-1, dim=2
+ ):
schema, tablename = self.getSchemaTableName(table)
sql = "SELECT AddGeometryColumn(%s, %s, %d, %s, %s)" % (
- self.quoteString(tablename), self.quoteString(geom_column), srid, self.quoteString(geom_type), dim)
+ self.quoteString(tablename),
+ self.quoteString(geom_column),
+ srid,
+ self.quoteString(geom_type),
+ dim,
+ )
self._execute_and_commit(sql)
def deleteGeometryColumn(self, table, geom_column):
return self.deleteTableColumn(table, geom_column)
def addTableUniqueConstraint(self, table, column):
- """Adds a unique constraint to a table """
+ """Adds a unique constraint to a table"""
return False # constraints not supported
def deleteTableConstraint(self, table, constraint):
- """Deletes constraint in a table """
+ """Deletes constraint in a table"""
return False # constraints not supported
def addTablePrimaryKey(self, table, column):
- """Adds a primery key (with one column) to a table """
- sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (self.quoteId(table), self.quoteId(column))
+ """Adds a primery key (with one column) to a table"""
+ sql = "ALTER TABLE {} ADD PRIMARY KEY ({})".format(
+ self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def createTableIndex(self, table, name, column, unique=False):
- """Creates index on one column using default options """
+ """Creates index on one column using default options"""
unique_str = "UNIQUE" if unique else ""
- sql = "CREATE %s INDEX %s ON %s (%s)" % (
- unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column))
+ sql = "CREATE {} INDEX {} ON {} ({})".format(
+ unique_str, self.quoteId(name), self.quoteId(table), self.quoteId(column)
+ )
self._execute_and_commit(sql)
def deleteTableIndex(self, table, name):
@@ -644,36 +719,43 @@ def deleteTableIndex(self, table, name):
sql = "DROP INDEX %s" % self.quoteId((schema, name))
self._execute_and_commit(sql)
- def createSpatialIndex(self, table, geom_column='geometry'):
+ def createSpatialIndex(self, table, geom_column="geometry"):
if self.isRasterTable(table):
return False
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT CreateSpatialIndex(%s, %s)" % (self.quoteString(tablename), self.quoteString(geom_column))
+ sql = "SELECT CreateSpatialIndex({}, {})".format(
+ self.quoteString(tablename), self.quoteString(geom_column)
+ )
self._execute_and_commit(sql)
- def deleteSpatialIndex(self, table, geom_column='geometry'):
+ def deleteSpatialIndex(self, table, geom_column="geometry"):
if self.isRasterTable(table):
return False
schema, tablename = self.getSchemaTableName(table)
try:
- sql = "SELECT DiscardSpatialIndex(%s, %s)" % (self.quoteString(tablename), self.quoteString(geom_column))
+ sql = "SELECT DiscardSpatialIndex({}, {})".format(
+ self.quoteString(tablename), self.quoteString(geom_column)
+ )
self._execute_and_commit(sql)
except DbError:
- sql = "SELECT DeleteSpatialIndex(%s, %s)" % (self.quoteString(tablename), self.quoteString(geom_column))
+ sql = "SELECT DeleteSpatialIndex({}, {})".format(
+ self.quoteString(tablename), self.quoteString(geom_column)
+ )
self._execute_and_commit(sql)
# delete the index table
- idx_table_name = "idx_%s_%s" % (tablename, geom_column)
+ idx_table_name = f"idx_{tablename}_{geom_column}"
self.deleteTable(idx_table_name)
- def hasSpatialIndex(self, table, geom_column='geometry'):
+ def hasSpatialIndex(self, table, geom_column="geometry"):
if not self.has_geometry_columns or self.isRasterTable(table):
return False
c = self._get_cursor()
schema, tablename = self.getSchemaTableName(table)
- sql = "SELECT spatial_index_enabled FROM geometry_columns WHERE upper(f_table_name) = upper(%s) AND upper(f_geometry_column) = upper(%s)" % (
- self.quoteString(tablename), self.quoteString(geom_column))
+ sql = "SELECT spatial_index_enabled FROM geometry_columns WHERE upper(f_table_name) = upper({}) AND upper(f_geometry_column) = upper({})".format(
+ self.quoteString(tablename), self.quoteString(geom_column)
+ )
self._execute(c, sql)
row = c.fetchone()
return row is not None and row[0] == 1
diff --git a/python/plugins/db_manager/db_plugins/spatialite/data_model.py b/python/plugins/db_manager/db_plugins/spatialite/data_model.py
index ceaf0b2b8a14..4eb7b519ceab 100644
--- a/python/plugins/db_manager/db_plugins/spatialite/data_model.py
+++ b/python/plugins/db_manager/db_plugins/spatialite/data_model.py
@@ -20,10 +20,12 @@
from qgis.core import QgsMessageLog
from ..plugin import BaseError
-from ..data_model import (TableDataModel,
- SqlResultModel,
- SqlResultModelAsync,
- SqlResultModelTask)
+from ..data_model import (
+ TableDataModel,
+ SqlResultModel,
+ SqlResultModelAsync,
+ SqlResultModelTask,
+)
from .plugin import SLDatabase
@@ -36,7 +38,7 @@ def __init__(self, table, parent=None):
table_txt = self.db.quoteId((self.table.schemaName(), self.table.name))
# run query and get results
- sql = "SELECT %s FROM %s" % (fields_txt, table_txt)
+ sql = f"SELECT {fields_txt} FROM {table_txt}"
c = self.db._get_cursor()
self.db._execute(c, sql)
@@ -57,7 +59,7 @@ def _sanitizeTableField(self, field):
if dataType[-10:] == "COLLECTION":
dataType = dataType[:-10]
if dataType in ["POINT", "LINESTRING", "POLYGON", "GEOMETRY"]:
- return 'GeometryType(%s)' % self.db.quoteId(field.name)
+ return "GeometryType(%s)" % self.db.quoteId(field.name)
return self.db.quoteId(field.name)
def rowCount(self, index=None):
diff --git a/python/plugins/db_manager/db_plugins/spatialite/info_model.py b/python/plugins/db_manager/db_plugins/spatialite/info_model.py
index c33314a5a053..4e326af51737 100644
--- a/python/plugins/db_manager/db_plugins/spatialite/info_model.py
+++ b/python/plugins/db_manager/db_plugins/spatialite/info_model.py
@@ -31,15 +31,16 @@ def __init__(self, db):
def connectionDetails(self):
tbl = [
- (QApplication.translate("DBManagerPlugin", "Filename:"), self.db.connector.dbname)
+ (
+ QApplication.translate("DBManagerPlugin", "Filename:"),
+ self.db.connector.dbname,
+ )
]
return HtmlTable(tbl)
def generalInfo(self):
info = self.db.connector.getInfo()
- tbl = [
- (QApplication.translate("DBManagerPlugin", "SQLite version:"), info[0])
- ]
+ tbl = [(QApplication.translate("DBManagerPlugin", "SQLite version:"), info[0])]
return HtmlTable(tbl)
def spatialInfo(self):
@@ -52,14 +53,20 @@ def spatialInfo(self):
tbl = [
(QApplication.translate("DBManagerPlugin", "Library:"), info[0]),
("GEOS:", info[1]),
- ("Proj:", info[2])
+ ("Proj:", info[2]),
]
ret.append(HtmlTable(tbl))
if not self.db.connector.has_geometry_columns:
- ret.append(HtmlParagraph(
- QApplication.translate("DBManagerPlugin", " geometry_columns table doesn't exist!\n"
- "This table is essential for many GIS applications for enumeration of tables.")))
+ ret.append(
+ HtmlParagraph(
+ QApplication.translate(
+ "DBManagerPlugin",
+ " geometry_columns table doesn't exist!\n"
+ "This table is essential for many GIS applications for enumeration of tables.",
+ )
+ )
+ )
return ret
diff --git a/python/plugins/db_manager/db_plugins/spatialite/plugin.py b/python/plugins/db_manager/db_plugins/spatialite/plugin.py
index f4a203755973..4cf7d90daf7d 100644
--- a/python/plugins/db_manager/db_plugins/spatialite/plugin.py
+++ b/python/plugins/db_manager/db_plugins/spatialite/plugin.py
@@ -27,8 +27,17 @@
from qgis.core import Qgis, QgsApplication, QgsDataSourceUri, QgsSettings
from qgis.gui import QgsMessageBar
-from ..plugin import DBPlugin, Database, Table, VectorTable, RasterTable, TableField, TableIndex, TableTrigger, \
- InvalidDataException
+from ..plugin import (
+ DBPlugin,
+ Database,
+ Table,
+ VectorTable,
+ RasterTable,
+ TableField,
+ TableIndex,
+ TableTrigger,
+ InvalidDataException,
+)
def classFactory():
@@ -43,19 +52,19 @@ def icon(self):
@classmethod
def typeName(self):
- return 'spatialite'
+ return "spatialite"
@classmethod
def typeNameString(self):
- return QCoreApplication.translate('db_manager', 'SpatiaLite')
+ return QCoreApplication.translate("db_manager", "SpatiaLite")
@classmethod
def providerName(self):
- return 'spatialite'
+ return "spatialite"
@classmethod
def connectionSettingsKey(self):
- return '/SpatiaLite/connections'
+ return "/SpatiaLite/connections"
def databasesFactory(self, connection, uri):
return SLDatabase(connection, uri)
@@ -63,10 +72,14 @@ def databasesFactory(self, connection, uri):
def connect(self, parent=None):
conn_name = self.connectionName()
settings = QgsSettings()
- settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), conn_name))
+ settings.beginGroup(f"/{self.connectionSettingsKey()}/{conn_name}")
if not settings.contains("sqlitepath"): # non-existent entry?
- raise InvalidDataException(self.tr('There is no defined database connection "{0}".').format(conn_name))
+ raise InvalidDataException(
+ self.tr('There is no defined database connection "{0}".').format(
+ conn_name
+ )
+ )
database = settings.value("sqlitepath")
@@ -77,7 +90,7 @@ def connect(self, parent=None):
@classmethod
def addConnection(self, conn_name, uri):
settings = QgsSettings()
- settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), conn_name))
+ settings.beginGroup(f"/{self.connectionSettingsKey()}/{conn_name}")
settings.setValue("sqlitepath", uri.database())
return True
@@ -85,7 +98,9 @@ def addConnection(self, conn_name, uri):
def addConnectionActionSlot(self, item, action, parent, index):
QApplication.restoreOverrideCursor()
try:
- filename, selected_filter = QFileDialog.getOpenFileName(parent, "Choose SQLite/SpatiaLite file")
+ filename, selected_filter = QFileDialog.getOpenFileName(
+ parent, "Choose SQLite/SpatiaLite file"
+ )
if not filename:
return
finally:
@@ -132,7 +147,9 @@ def sqlResultModelAsync(self, sql, parent):
def registerDatabaseActions(self, mainWindow):
action = QAction(self.tr("Run &Vacuum"), self)
- mainWindow.registerAction(action, self.tr("&Database"), self.runVacuumActionSlot)
+ mainWindow.registerAction(
+ action, self.tr("&Database"), self.runVacuumActionSlot
+ )
Database.registerDatabaseActions(self, mainWindow)
@@ -140,8 +157,11 @@ def runVacuumActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
if not isinstance(item, (DBPlugin, Table)) or item.database() is None:
- parent.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
- Qgis.MessageLevel.Info, parent.iface.messageTimeout())
+ parent.infoBar.pushMessage(
+ self.tr("No database selected or you are not connected to it."),
+ Qgis.MessageLevel.Info,
+ parent.iface.messageTimeout(),
+ )
return
finally:
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -170,7 +190,9 @@ def explicitSpatialIndex(self):
return True
def spatialIndexClause(self, src_table, src_column, dest_table, dest_column):
- return """ "%s".ROWID IN (\nSELECT ROWID FROM SpatialIndex WHERE f_table_name='%s' AND search_frame="%s"."%s") """ % (src_table, src_table, dest_table, dest_column)
+ return """ "{}".ROWID IN (\nSELECT ROWID FROM SpatialIndex WHERE f_table_name='{}' AND search_frame="{}"."{}") """.format(
+ src_table, src_table, dest_table, dest_column
+ )
def supportsComment(self):
return False
@@ -183,7 +205,7 @@ def __init__(self, row, db, schema=None):
self.name, self.isView, self.isSysTable = row
def ogrUri(self):
- ogrUri = "%s|layername=%s" % (self.uri().database(), self.name)
+ ogrUri = f"{self.uri().database()}|layername={self.name}"
return ogrUri
def mimeUri(self):
@@ -220,16 +242,20 @@ def __init__(self, row, db, schema=None):
# SpatiaLite does case-insensitive checks for table names, but the
# SL provider didn't do the same in QGIS < 1.9, so self.geomTableName
# stores the table name like stored in the geometry_columns table
- self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[-5:]
+ self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = (
+ row[-5:]
+ )
def uri(self):
uri = self.database().uri()
- uri.setDataSource('', self.geomTableName, self.geomColumn)
+ uri.setDataSource("", self.geomTableName, self.geomColumn)
return uri
def hasSpatialIndex(self, geom_column=None):
geom_column = geom_column if geom_column is not None else self.geomColumn
- return self.database().connector.hasSpatialIndex((self.schemaName(), self.name), geom_column)
+ return self.database().connector.hasSpatialIndex(
+ (self.schemaName(), self.name), geom_column
+ )
def createSpatialIndex(self, geom_column=None):
self.aboutToChange.emit()
@@ -260,19 +286,21 @@ def __init__(self, row, db, schema=None):
SLTable.__init__(self, row[:-3], db, schema)
RasterTable.__init__(self, db, schema)
self.prefixName, self.geomColumn, self.srid = row[-3:]
- self.geomType = 'RASTER'
+ self.geomType = "RASTER"
# def info(self):
# from .info_model import SLRasterTableInfo
# return SLRasterTableInfo(self)
def rasterliteGdalUri(self):
- gdalUri = 'RASTERLITE:%s,table=%s' % (self.uri().database(), self.prefixName)
+ gdalUri = "RASTERLITE:{},table={}".format(
+ self.uri().database(), self.prefixName
+ )
return gdalUri
def mimeUri(self):
# QGIS has no provider to load rasters, let's use GDAL
- uri = "raster:gdal:%s:%s" % (self.name, self.uri().database())
+ uri = f"raster:gdal:{self.name}:{self.uri().database()}"
return uri
def toMapLayer(self, geometryType=None, crs=None):
@@ -283,7 +311,9 @@ def toMapLayer(self, geometryType=None, crs=None):
rl = QgsRasterLayer(uri, self.name)
if rl.isValid():
- rl.setContrastEnhancement(QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum)
+ rl.setContrastEnhancement(
+ QgsContrastEnhancement.ContrastEnhancementAlgorithm.StretchToMinimumMaximum
+ )
return rl
@@ -291,7 +321,14 @@ class SLTableField(TableField):
def __init__(self, row, table):
TableField.__init__(self, table)
- self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
+ (
+ self.num,
+ self.name,
+ self.dataType,
+ self.notNull,
+ self.default,
+ self.primaryKey,
+ ) = row
self.hasDefault = self.default
diff --git a/python/plugins/db_manager/db_plugins/spatialite/sql_dictionary.py b/python/plugins/db_manager/db_plugins/spatialite/sql_dictionary.py
index 9fb8b253d109..8b2feb51feec 100644
--- a/python/plugins/db_manager/db_plugins/spatialite/sql_dictionary.py
+++ b/python/plugins/db_manager/db_plugins/spatialite/sql_dictionary.py
@@ -15,107 +15,347 @@
***************************************************************************
"""
-__author__ = 'Giuseppe Sucameli'
-__date__ = 'April 2012'
-__copyright__ = '(C) 2012, Giuseppe Sucameli'
+__author__ = "Giuseppe Sucameli"
+__date__ = "April 2012"
+__copyright__ = "(C) 2012, Giuseppe Sucameli"
# keywords
keywords = [
# TODO get them from a reference page
- "action", "add", "after", "all", "alter", "analyze", "and", "as", "asc",
- "before", "begin", "between", "by", "cascade", "case", "cast", "check",
- "collate", "column", "commit", "constraint", "create", "cross", "current_date",
- "current_time", "current_timestamp", "default", "deferrable", "deferred",
- "delete", "desc", "distinct", "drop", "each", "else", "end", "escape",
- "except", "exists", "for", "foreign", "from", "full", "group", "having",
- "ignore", "immediate", "in", "initially", "inner", "insert", "intersect",
- "into", "is", "isnull", "join", "key", "left", "like", "limit", "match",
- "natural", "no", "not", "notnull", "null", "of", "offset", "on", "or", "order",
- "outer", "primary", "references", "release", "restrict", "right", "rollback",
- "row", "savepoint", "select", "set", "table", "temporary", "then", "to",
- "transaction", "trigger", "union", "unique", "update", "using", "values",
- "view", "when", "where",
-
- "abort", "attach", "autoincrement", "conflict", "database", "detach",
- "exclusive", "explain", "fail", "glob", "if", "index", "indexed", "instead",
- "plan", "pragma", "query", "raise", "regexp", "reindex", "rename", "replace",
- "temp", "vacuum", "virtual"
+ "action",
+ "add",
+ "after",
+ "all",
+ "alter",
+ "analyze",
+ "and",
+ "as",
+ "asc",
+ "before",
+ "begin",
+ "between",
+ "by",
+ "cascade",
+ "case",
+ "cast",
+ "check",
+ "collate",
+ "column",
+ "commit",
+ "constraint",
+ "create",
+ "cross",
+ "current_date",
+ "current_time",
+ "current_timestamp",
+ "default",
+ "deferrable",
+ "deferred",
+ "delete",
+ "desc",
+ "distinct",
+ "drop",
+ "each",
+ "else",
+ "end",
+ "escape",
+ "except",
+ "exists",
+ "for",
+ "foreign",
+ "from",
+ "full",
+ "group",
+ "having",
+ "ignore",
+ "immediate",
+ "in",
+ "initially",
+ "inner",
+ "insert",
+ "intersect",
+ "into",
+ "is",
+ "isnull",
+ "join",
+ "key",
+ "left",
+ "like",
+ "limit",
+ "match",
+ "natural",
+ "no",
+ "not",
+ "notnull",
+ "null",
+ "of",
+ "offset",
+ "on",
+ "or",
+ "order",
+ "outer",
+ "primary",
+ "references",
+ "release",
+ "restrict",
+ "right",
+ "rollback",
+ "row",
+ "savepoint",
+ "select",
+ "set",
+ "table",
+ "temporary",
+ "then",
+ "to",
+ "transaction",
+ "trigger",
+ "union",
+ "unique",
+ "update",
+ "using",
+ "values",
+ "view",
+ "when",
+ "where",
+ "abort",
+ "attach",
+ "autoincrement",
+ "conflict",
+ "database",
+ "detach",
+ "exclusive",
+ "explain",
+ "fail",
+ "glob",
+ "if",
+ "index",
+ "indexed",
+ "instead",
+ "plan",
+ "pragma",
+ "query",
+ "raise",
+ "regexp",
+ "reindex",
+ "rename",
+ "replace",
+ "temp",
+ "vacuum",
+ "virtual",
]
spatialite_keywords = []
# functions
functions = [
# TODO get them from a reference page
- "changes", "coalesce", "glob", "ifnull", "hex", "last_insert_rowid",
- "nullif", "quote", "random",
- "randomblob", "replace", "round", "soundex", "total_change",
- "typeof", "zeroblob", "date", "datetime", "julianday", "strftime"
+ "changes",
+ "coalesce",
+ "glob",
+ "ifnull",
+ "hex",
+ "last_insert_rowid",
+ "nullif",
+ "quote",
+ "random",
+ "randomblob",
+ "replace",
+ "round",
+ "soundex",
+ "total_change",
+ "typeof",
+ "zeroblob",
+ "date",
+ "datetime",
+ "julianday",
+ "strftime",
]
operators = [
- ' AND ', ' OR ', '||', ' < ', ' <= ', ' > ', ' >= ', ' = ', ' <> ', ' IS ', ' IS NOT ', ' IN ', ' LIKE ', ' GLOB ', ' MATCH ', ' REGEXP '
+ " AND ",
+ " OR ",
+ "||",
+ " < ",
+ " <= ",
+ " > ",
+ " >= ",
+ " = ",
+ " <> ",
+ " IS ",
+ " IS NOT ",
+ " IN ",
+ " LIKE ",
+ " GLOB ",
+ " MATCH ",
+ " REGEXP ",
]
math_functions = [
# SQL math functions
- "Abs", "ACos", "ASin", "ATan", "Cos", "Cot", "Degrees", "Exp", "Floor", "Log", "Log2",
- "Log10", "Pi", "Radians", "Round", "Sign", "Sin", "Sqrt", "StdDev_Pop", "StdDev_Samp", "Tan",
- "Var_Pop", "Var_Samp"]
+ "Abs",
+ "ACos",
+ "ASin",
+ "ATan",
+ "Cos",
+ "Cot",
+ "Degrees",
+ "Exp",
+ "Floor",
+ "Log",
+ "Log2",
+ "Log10",
+ "Pi",
+ "Radians",
+ "Round",
+ "Sign",
+ "Sin",
+ "Sqrt",
+ "StdDev_Pop",
+ "StdDev_Samp",
+ "Tan",
+ "Var_Pop",
+ "Var_Samp",
+]
-string_functions = ["Length", "Lower", "Upper", "Like", "Trim", "LTrim", "RTrim", "Replace", "Substr"]
+string_functions = [
+ "Length",
+ "Lower",
+ "Upper",
+ "Like",
+ "Trim",
+ "LTrim",
+ "RTrim",
+ "Replace",
+ "Substr",
+]
aggregate_functions = [
- "Max", "Min", "Avg", "Count", "Sum", "Group_Concat", "Total", "Var_Pop", "Var_Samp", "StdDev_Pop", "StdDev_Samp"
+ "Max",
+ "Min",
+ "Avg",
+ "Count",
+ "Sum",
+ "Group_Concat",
+ "Total",
+ "Var_Pop",
+ "Var_Samp",
+ "StdDev_Pop",
+ "StdDev_Samp",
]
spatialite_functions = [ # from www.gaia-gis.it/spatialite-2.3.0/spatialite-sql-2.3.0.html
- # SQL utility functions for BLOB objects
- "*iszipblob", "*ispdfblob", "*isgifblob", "*ispngblob", "*isjpegblob", "*isexifblob",
- "*isexifgpsblob", "*geomfromexifgpsblob", "MakePoint", "BuildMbr", "*buildcirclembr", "ST_MinX",
- "ST_MinY", "ST_MaxX", "ST_MaxY",
- # SQL functions for constructing a geometric object given its Well-known Text Representation
- "ST_GeomFromText", "*pointfromtext",
- # SQL functions for constructing a geometric object given its Well-known Binary Representation
- "*geomfromwkb", "*pointfromwkb",
- # SQL functions for obtaining the Well-known Text / Well-known Binary Representation of a geometric object
- "ST_AsText", "ST_AsBinary",
- # SQL functions supporting exotic geometric formats
- "*assvg", "*asfgf", "*geomfromfgf",
- # SQL functions on type Geometry
- "ST_Dimension", "ST_GeometryType", "ST_Srid", "ST_SetSrid", "ST_isEmpty", "ST_isSimple", "ST_isValid", "ST_Boundary",
- "ST_Envelope",
- # SQL functions on type Point
- "ST_X", "ST_Y",
- # SQL functions on type Curve [Linestring or Ring]
- "ST_StartPoint", "ST_EndPoint", "ST_Length", "ST_isClosed", "ST_isRing", "ST_Simplify",
- "*simplifypreservetopology",
- # SQL functions on type LineString
- "ST_NumPoints", "ST_PointN",
- # SQL functions on type Surface [Polygon or Ring]
- "ST_Centroid", "ST_PointOnSurface", "ST_Area",
- # SQL functions on type Polygon
- "ST_ExteriorRing", "ST_InteriorRingN",
- # SQL functions on type GeomCollection
- "ST_NumGeometries", "ST_GeometryN",
- # SQL functions that test approximative spatial relationships via MBRs
- "MbrEqual", "MbrDisjoint", "MbrTouches", "MbrWithin", "MbrOverlaps", "MbrIntersects",
- "MbrContains",
- # SQL functions that test spatial relationships
- "ST_Equals", "ST_Disjoint", "ST_Touches", "ST_Within", "ST_Overlaps", "ST_Crosses", "ST_Intersects", "ST_Contains",
- "ST_Relate",
- # SQL functions for distance relationships
- "ST_Distance",
- # SQL functions that implement spatial operators
- "ST_Intersection", "ST_Difference", "ST_Union", "ST_SymDifference", "ST_Buffer", "ST_ConvexHull",
- # SQL functions for coordinate transformations
- "ST_Transform",
- # SQL functions for Spatial-MetaData and Spatial-Index handling
- "*initspatialmetadata", "*addgeometrycolumn", "*recovergeometrycolumn", "*discardgeometrycolumn",
- "*createspatialindex", "*creatembrcache", "*disablespatialindex",
- # SQL functions implementing FDO/OGR compatibility
- "*checkspatialmetadata", "*autofdostart", "*autofdostop", "*initfdospatialmetadata",
- "*addfdogeometrycolumn", "*recoverfdogeometrycolumn", "*discardfdogeometrycolumn",
- # SQL functions for MbrCache-based queries
- "*filtermbrwithin", "*filtermbrcontains", "*filtermbrintersects", "*buildmbrfilter"
+ # SQL utility functions for BLOB objects
+ "*iszipblob",
+ "*ispdfblob",
+ "*isgifblob",
+ "*ispngblob",
+ "*isjpegblob",
+ "*isexifblob",
+ "*isexifgpsblob",
+ "*geomfromexifgpsblob",
+ "MakePoint",
+ "BuildMbr",
+ "*buildcirclembr",
+ "ST_MinX",
+ "ST_MinY",
+ "ST_MaxX",
+ "ST_MaxY",
+ # SQL functions for constructing a geometric object given its Well-known Text Representation
+ "ST_GeomFromText",
+ "*pointfromtext",
+ # SQL functions for constructing a geometric object given its Well-known Binary Representation
+ "*geomfromwkb",
+ "*pointfromwkb",
+ # SQL functions for obtaining the Well-known Text / Well-known Binary Representation of a geometric object
+ "ST_AsText",
+ "ST_AsBinary",
+ # SQL functions supporting exotic geometric formats
+ "*assvg",
+ "*asfgf",
+ "*geomfromfgf",
+ # SQL functions on type Geometry
+ "ST_Dimension",
+ "ST_GeometryType",
+ "ST_Srid",
+ "ST_SetSrid",
+ "ST_isEmpty",
+ "ST_isSimple",
+ "ST_isValid",
+ "ST_Boundary",
+ "ST_Envelope",
+ # SQL functions on type Point
+ "ST_X",
+ "ST_Y",
+ # SQL functions on type Curve [Linestring or Ring]
+ "ST_StartPoint",
+ "ST_EndPoint",
+ "ST_Length",
+ "ST_isClosed",
+ "ST_isRing",
+ "ST_Simplify",
+ "*simplifypreservetopology",
+ # SQL functions on type LineString
+ "ST_NumPoints",
+ "ST_PointN",
+ # SQL functions on type Surface [Polygon or Ring]
+ "ST_Centroid",
+ "ST_PointOnSurface",
+ "ST_Area",
+ # SQL functions on type Polygon
+ "ST_ExteriorRing",
+ "ST_InteriorRingN",
+ # SQL functions on type GeomCollection
+ "ST_NumGeometries",
+ "ST_GeometryN",
+ # SQL functions that test approximative spatial relationships via MBRs
+ "MbrEqual",
+ "MbrDisjoint",
+ "MbrTouches",
+ "MbrWithin",
+ "MbrOverlaps",
+ "MbrIntersects",
+ "MbrContains",
+ # SQL functions that test spatial relationships
+ "ST_Equals",
+ "ST_Disjoint",
+ "ST_Touches",
+ "ST_Within",
+ "ST_Overlaps",
+ "ST_Crosses",
+ "ST_Intersects",
+ "ST_Contains",
+ "ST_Relate",
+ # SQL functions for distance relationships
+ "ST_Distance",
+ # SQL functions that implement spatial operators
+ "ST_Intersection",
+ "ST_Difference",
+ "ST_Union",
+ "ST_SymDifference",
+ "ST_Buffer",
+ "ST_ConvexHull",
+ # SQL functions for coordinate transformations
+ "ST_Transform",
+ # SQL functions for Spatial-MetaData and Spatial-Index handling
+ "*initspatialmetadata",
+ "*addgeometrycolumn",
+ "*recovergeometrycolumn",
+ "*discardgeometrycolumn",
+ "*createspatialindex",
+ "*creatembrcache",
+ "*disablespatialindex",
+ # SQL functions implementing FDO/OGR compatibility
+ "*checkspatialmetadata",
+ "*autofdostart",
+ "*autofdostop",
+ "*initfdospatialmetadata",
+ "*addfdogeometrycolumn",
+ "*recoverfdogeometrycolumn",
+ "*discardfdogeometrycolumn",
+ # SQL functions for MbrCache-based queries
+ "*filtermbrwithin",
+ "*filtermbrcontains",
+ "*filtermbrintersects",
+ "*buildmbrfilter",
]
# constants
@@ -125,7 +365,7 @@
def getSqlDictionary(spatial=True):
def strip_star(s):
- if s[0] == '*':
+ if s[0] == "*":
return s.lower()[1:]
else:
return s.lower()
@@ -137,20 +377,26 @@ def strip_star(s):
f += spatialite_functions
c += spatialite_constants
- return {'keyword': list(map(strip_star, k)), 'constant': list(map(strip_star, c)), 'function': list(map(strip_star, f))}
+ return {
+ "keyword": list(map(strip_star, k)),
+ "constant": list(map(strip_star, c)),
+ "function": list(map(strip_star, f)),
+ }
def getQueryBuilderDictionary():
# concat functions
def ff(l):
- return [s for s in l if s[0] != '*']
+ return [s for s in l if s[0] != "*"]
def add_paren(l):
return [s + "(" for s in l]
- foo = sorted(add_paren(ff(list(set.union(set(functions), set(spatialite_functions))))))
+ foo = sorted(
+ add_paren(ff(list(set.union(set(functions), set(spatialite_functions)))))
+ )
m = sorted(add_paren(ff(math_functions)))
agg = sorted(add_paren(ff(aggregate_functions)))
op = ff(operators)
s = sorted(add_paren(ff(string_functions)))
- return {'function': foo, 'math': m, 'aggregate': agg, 'operator': op, 'string': s}
+ return {"function": foo, "math": m, "aggregate": agg, "operator": op, "string": s}
diff --git a/python/plugins/db_manager/db_plugins/vlayers/connector.py b/python/plugins/db_manager/db_plugins/vlayers/connector.py
index 13bc9c505ce7..29e117c04fde 100644
--- a/python/plugins/db_manager/db_plugins/vlayers/connector.py
+++ b/python/plugins/db_manager/db_plugins/vlayers/connector.py
@@ -29,7 +29,7 @@
QgsMapLayerType,
QgsVectorLayer,
QgsCoordinateReferenceSystem,
- QgsWkbTypes
+ QgsWkbTypes,
)
import sqlite3
@@ -172,31 +172,40 @@ def hasTableColumnEditingSupport(self):
def fieldTypes(self):
return [
- "integer", "bigint", "smallint", # integers
- "real", "double", "float", "numeric", # floats
- "varchar", "varchar(255)", "character(20)", "text", # strings
- "date", "datetime" # date/time
+ "integer",
+ "bigint",
+ "smallint", # integers
+ "real",
+ "double",
+ "float",
+ "numeric", # floats
+ "varchar",
+ "varchar(255)",
+ "character(20)",
+ "text", # strings
+ "date",
+ "datetime", # date/time
]
def getSchemas(self):
return None
def getTables(self, schema=None, add_sys_tables=False):
- """ get list of tables """
+ """get list of tables"""
return self.getVectorTables()
def getVectorTables(self, schema=None):
- """ get list of table with a geometry column
- it returns:
- name (table name)
- is_system_table
- type = 'view' (is a view?)
- geometry_column:
- f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
- f_geometry_column
- type
- coord_dimension
- srid
+ """get list of table with a geometry column
+ it returns:
+ name (table name)
+ is_system_table
+ type = 'view' (is a view?)
+ geometry_column:
+ f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
+ f_geometry_column
+ type
+ coord_dimension
+ srid
"""
reg = VLayerRegistry.instance()
VLayerRegistry.instance().reset()
@@ -218,16 +227,27 @@ def getVectorTables(self, schema=None):
g_flat = QgsWkbTypes.flatType(g)
geomType = QgsWkbTypes.displayString(g_flat).upper()
if geomType:
- dim = 'XY'
+ dim = "XY"
if QgsWkbTypes.hasZ(g):
- dim += 'Z'
+ dim += "Z"
if QgsWkbTypes.hasM(g):
- dim += 'M'
+ dim += "M"
srid = l.crs().postgisSrid()
if srid not in self.mapSridToName:
self.mapSridToName[srid] = l.crs().description()
lst.append(
- (Table.VectorType, lname, False, False, l.id(), 'geometry', geomType, dim, srid))
+ (
+ Table.VectorType,
+ lname,
+ False,
+ False,
+ l.id(),
+ "geometry",
+ geomType,
+ dim,
+ srid,
+ )
+ )
else:
lst.append((Table.TableType, lname, False, False))
return lst
@@ -243,15 +263,17 @@ def getTableRowCount(self, table):
return l.featureCount()
def getTableFields(self, table):
- """ return list of columns in table """
+ """return list of columns in table"""
t = table[1]
l = VLayerRegistry.instance().getLayer(t)
if not l or not l.isValid():
return []
# id, name, type, nonnull, default, pk
n = l.dataProvider().fields().size()
- f = [(i, f.name(), f.typeName(), False, None, False)
- for i, f in enumerate(l.dataProvider().fields())]
+ f = [
+ (i, f.name(), f.typeName(), False, None, False)
+ for i, f in enumerate(l.dataProvider().fields())
+ ]
if l.isSpatial():
f += [(n, "geometry", "geometry", False, None, False)]
return f
@@ -335,7 +357,16 @@ def addTableColumn(self, table, field_def):
def deleteTableColumn(self, table, column):
print("**unimplemented** deleteTableColumn")
- def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not_null=None, new_default=None, comment=None):
+ def updateTableColumn(
+ self,
+ table,
+ column,
+ new_name,
+ new_data_type=None,
+ new_not_null=None,
+ new_default=None,
+ comment=None,
+ ):
print("**unimplemented** updateTableColumn")
def renameTableColumn(self, table, column, new_name):
@@ -358,7 +389,9 @@ def isGeometryColumn(self, table, column):
print("**unimplemented** isGeometryColumn")
return False
- def addGeometryColumn(self, table, geom_column='geometry', geom_type='POINT', srid=-1, dim=2):
+ def addGeometryColumn(
+ self, table, geom_column="geometry", geom_type="POINT", srid=-1, dim=2
+ ):
print("**unimplemented** addGeometryColumn")
return False
@@ -386,15 +419,15 @@ def deleteTableIndex(self, table, name):
print("**unimplemented** deleteTableIndex")
return False
- def createSpatialIndex(self, table, geom_column='geometry'):
+ def createSpatialIndex(self, table, geom_column="geometry"):
print("**unimplemented** createSpatialIndex")
return False
- def deleteSpatialIndex(self, table, geom_column='geometry'):
+ def deleteSpatialIndex(self, table, geom_column="geometry"):
print("**unimplemented** deleteSpatialIndex")
return False
- def hasSpatialIndex(self, table, geom_column='geometry'):
+ def hasSpatialIndex(self, table, geom_column="geometry"):
print("**unimplemented** hasSpatialIndex")
return False
@@ -408,6 +441,7 @@ def connection_error_types(self):
def getSqlDictionary(self):
from .sql_dictionary import getSqlDictionary
+
sql_dict = getSqlDictionary()
items = []
diff --git a/python/plugins/db_manager/db_plugins/vlayers/data_model.py b/python/plugins/db_manager/db_plugins/vlayers/data_model.py
index 22b28d1686ff..4195f69778c9 100644
--- a/python/plugins/db_manager/db_plugins/vlayers/data_model.py
+++ b/python/plugins/db_manager/db_plugins/vlayers/data_model.py
@@ -17,21 +17,25 @@
***************************************************************************/
"""
-from ..data_model import (TableDataModel,
- BaseTableModel,
- SqlResultModelAsync,
- SqlResultModelTask)
+from ..data_model import (
+ TableDataModel,
+ BaseTableModel,
+ SqlResultModelAsync,
+ SqlResultModelTask,
+)
from .connector import VLayerRegistry, getQueryGeometryName
from .plugin import LVectorTable
from ..plugin import DbError, BaseError
from qgis.PyQt.QtCore import QElapsedTimer, QTemporaryFile
-from qgis.core import (QgsVectorLayer,
- QgsWkbTypes,
- QgsVirtualLayerDefinition,
- QgsVirtualLayerTask,
- QgsTask)
+from qgis.core import (
+ QgsVectorLayer,
+ QgsWkbTypes,
+ QgsVirtualLayerDefinition,
+ QgsVirtualLayerTask,
+ QgsTask,
+)
class LTableDataModel(TableDataModel):
@@ -56,7 +60,7 @@ def __init__(self, table, parent=None):
if f.hasGeometry():
a.append(QgsWkbTypes.displayString(f.geometry().wkbType()))
else:
- a.append('None')
+ a.append("None")
self.resdata.append(a)
self.fetchedFrom = 0
@@ -83,7 +87,9 @@ def __init__(self, db, sql, parent):
df.setQuery(sql)
self.subtask = QgsVirtualLayerTask(df)
- self.addSubTask(self.subtask, [], QgsTask.SubTaskDependency.ParentDependsOnSubTask)
+ self.addSubTask(
+ self.subtask, [], QgsTask.SubTaskDependency.ParentDependsOnSubTask
+ )
def run(self):
try:
diff --git a/python/plugins/db_manager/db_plugins/vlayers/info_model.py b/python/plugins/db_manager/db_plugins/vlayers/info_model.py
index 473caab0cff1..e5e4346c741f 100644
--- a/python/plugins/db_manager/db_plugins/vlayers/info_model.py
+++ b/python/plugins/db_manager/db_plugins/vlayers/info_model.py
@@ -29,15 +29,12 @@ def __init__(self, db):
self.db = db
def connectionDetails(self):
- tbl = [
- ]
+ tbl = []
return HtmlTable(tbl)
def generalInfo(self):
self.db.connector.getInfo()
- tbl = [
- (QApplication.translate("DBManagerPlugin", "SQLite version:"), "3")
- ]
+ tbl = [(QApplication.translate("DBManagerPlugin", "SQLite version:"), "3")]
return HtmlTable(tbl)
def privilegesDetails(self):
diff --git a/python/plugins/db_manager/db_plugins/vlayers/plugin.py b/python/plugins/db_manager/db_plugins/vlayers/plugin.py
index 19ba23fdd1d8..f9109008f1d4 100644
--- a/python/plugins/db_manager/db_plugins/vlayers/plugin.py
+++ b/python/plugins/db_manager/db_plugins/vlayers/plugin.py
@@ -22,7 +22,12 @@
from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtGui import QIcon
-from qgis.core import QgsApplication, QgsVectorLayer, QgsProject, QgsVirtualLayerDefinition
+from qgis.core import (
+ QgsApplication,
+ QgsVectorLayer,
+ QgsProject,
+ QgsVirtualLayerDefinition,
+)
from ..plugin import DBPlugin, Database, Table, VectorTable, TableField
@@ -42,23 +47,25 @@ def connectionIcon(self):
@classmethod
def typeName(self):
- return 'vlayers'
+ return "vlayers"
@classmethod
def typeNameString(self):
- return QCoreApplication.translate('db_manager', 'Virtual Layers')
+ return QCoreApplication.translate("db_manager", "Virtual Layers")
@classmethod
def providerName(self):
- return 'virtual'
+ return "virtual"
@classmethod
def connectionSettingsKey(self):
- return 'vlayers'
+ return "vlayers"
@classmethod
def connections(self):
- return [VLayerDBPlugin(QCoreApplication.translate('db_manager', 'Project layers'))]
+ return [
+ VLayerDBPlugin(QCoreApplication.translate("db_manager", "Project layers"))
+ ]
def databasesFactory(self, connection, uri):
return FakeDatabase(connection, uri)
@@ -92,17 +99,29 @@ def rasterTablesFactory(self, row, db, schema=None):
def info(self):
from .info_model import LDatabaseInfo
+
return LDatabaseInfo(self)
def sqlResultModel(self, sql, parent):
from .data_model import LSqlResultModel
+
return LSqlResultModel(self, sql, parent)
def sqlResultModelAsync(self, sql, parent):
from .data_model import LSqlResultModelAsync
+
return LSqlResultModelAsync(self, sql, parent)
- def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, _filter=""):
+ def toSqlLayer(
+ self,
+ sql,
+ geomCol,
+ uniqueCol,
+ layerName="QueryLayer",
+ layerType=None,
+ avoidSelectById=False,
+ _filter="",
+ ):
df = QgsVirtualLayerDefinition()
df.setQuery(sql)
if uniqueCol is not None:
@@ -128,7 +147,9 @@ def explicitSpatialIndex(self):
return True
def spatialIndexClause(self, src_table, src_column, dest_table, dest_column):
- return '"%s"._search_frame_ = "%s"."%s"' % (src_table, dest_table, dest_column)
+ return '"{}"._search_frame_ = "{}"."{}"'.format(
+ src_table, dest_table, dest_column
+ )
def supportsComment(self):
return False
@@ -145,6 +166,7 @@ def tableFieldsFactory(self, row, table):
def tableDataModel(self, parent):
from .data_model import LTableDataModel
+
return LTableDataModel(self, parent)
def canBeAddedToCanvas(self):
@@ -159,12 +181,13 @@ def __init__(self, row, db, schema=None):
# SpatiaLite does case-insensitive checks for table names, but the
# SL provider didn't do the same in QGIS < 1.9, so self.geomTableName
# stores the table name like stored in the geometry_columns table
- self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[
- -5:]
+ self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = (
+ row[-5:]
+ )
def uri(self):
uri = self.database().uri()
- uri.setDataSource('', self.geomTableName, self.geomColumn)
+ uri.setDataSource("", self.geomTableName, self.geomColumn)
return uri
def hasSpatialIndex(self, geom_column=None):
@@ -178,7 +201,8 @@ def deleteSpatialIndex(self, geom_column=None):
def refreshTableEstimatedExtent(self):
self.extent = self.database().connector.getTableExtent(
- ("id", self.geomTableName), None)
+ ("id", self.geomTableName), None
+ )
def runAction(self, action):
return
@@ -191,5 +215,12 @@ class LTableField(TableField):
def __init__(self, row, table):
TableField.__init__(self, table)
- self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
+ (
+ self.num,
+ self.name,
+ self.dataType,
+ self.notNull,
+ self.default,
+ self.primaryKey,
+ ) = row
self.hasDefault = self.default
diff --git a/python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py b/python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py
index 0b177b52ce9e..6103273f9945 100644
--- a/python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py
+++ b/python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py
@@ -15,122 +15,472 @@
***************************************************************************
"""
-__author__ = 'Hugo Mercier'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Hugo Mercier'
+__author__ = "Hugo Mercier"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Hugo Mercier"
# keywords
keywords = [
# TODO get them from a reference page
- "action", "add", "after", "all", "alter", "analyze", "and", "as", "asc",
- "before", "begin", "between", "by", "cascade", "case", "cast", "check",
- "collate", "column", "commit", "constraint", "create", "cross", "current_date",
- "current_time", "current_timestamp", "default", "deferrable", "deferred",
- "delete", "desc", "distinct", "drop", "each", "else", "end", "escape",
- "except", "exists", "for", "foreign", "from", "full", "group", "having",
- "ignore", "immediate", "in", "initially", "inner", "insert", "intersect",
- "into", "is", "isnull", "join", "key", "left", "like", "limit", "match",
- "natural", "no", "not", "notnull", "null", "of", "offset", "on", "or", "order",
- "outer", "primary", "references", "release", "restrict", "right", "rollback",
- "row", "savepoint", "select", "set", "table", "temporary", "then", "to",
- "transaction", "trigger", "union", "unique", "update", "using", "values",
- "view", "when", "where",
-
- "abort", "attach", "autoincrement", "conflict", "database", "detach",
- "exclusive", "explain", "fail", "glob", "if", "index", "indexed", "instead",
- "plan", "pragma", "query", "raise", "regexp", "reindex", "rename", "replace",
- "temp", "vacuum", "virtual"
+ "action",
+ "add",
+ "after",
+ "all",
+ "alter",
+ "analyze",
+ "and",
+ "as",
+ "asc",
+ "before",
+ "begin",
+ "between",
+ "by",
+ "cascade",
+ "case",
+ "cast",
+ "check",
+ "collate",
+ "column",
+ "commit",
+ "constraint",
+ "create",
+ "cross",
+ "current_date",
+ "current_time",
+ "current_timestamp",
+ "default",
+ "deferrable",
+ "deferred",
+ "delete",
+ "desc",
+ "distinct",
+ "drop",
+ "each",
+ "else",
+ "end",
+ "escape",
+ "except",
+ "exists",
+ "for",
+ "foreign",
+ "from",
+ "full",
+ "group",
+ "having",
+ "ignore",
+ "immediate",
+ "in",
+ "initially",
+ "inner",
+ "insert",
+ "intersect",
+ "into",
+ "is",
+ "isnull",
+ "join",
+ "key",
+ "left",
+ "like",
+ "limit",
+ "match",
+ "natural",
+ "no",
+ "not",
+ "notnull",
+ "null",
+ "of",
+ "offset",
+ "on",
+ "or",
+ "order",
+ "outer",
+ "primary",
+ "references",
+ "release",
+ "restrict",
+ "right",
+ "rollback",
+ "row",
+ "savepoint",
+ "select",
+ "set",
+ "table",
+ "temporary",
+ "then",
+ "to",
+ "transaction",
+ "trigger",
+ "union",
+ "unique",
+ "update",
+ "using",
+ "values",
+ "view",
+ "when",
+ "where",
+ "abort",
+ "attach",
+ "autoincrement",
+ "conflict",
+ "database",
+ "detach",
+ "exclusive",
+ "explain",
+ "fail",
+ "glob",
+ "if",
+ "index",
+ "indexed",
+ "instead",
+ "plan",
+ "pragma",
+ "query",
+ "raise",
+ "regexp",
+ "reindex",
+ "rename",
+ "replace",
+ "temp",
+ "vacuum",
+ "virtual",
]
spatialite_keywords = []
# functions
functions = [
# TODO get them from a reference page
- "changes", "coalesce", "glob", "ifnull", "hex", "last_insert_rowid",
- "nullif", "quote", "random",
- "randomblob", "replace", "round", "soundex", "total_change",
- "typeof", "zeroblob", "date", "datetime", "julianday", "strftime"
+ "changes",
+ "coalesce",
+ "glob",
+ "ifnull",
+ "hex",
+ "last_insert_rowid",
+ "nullif",
+ "quote",
+ "random",
+ "randomblob",
+ "replace",
+ "round",
+ "soundex",
+ "total_change",
+ "typeof",
+ "zeroblob",
+ "date",
+ "datetime",
+ "julianday",
+ "strftime",
]
operators = [
- ' AND ', ' OR ', '||', ' < ', ' <= ', ' > ', ' >= ', ' = ', ' <> ', ' IS ', ' IS NOT ', ' IN ', ' LIKE ', ' GLOB ', ' MATCH ', ' REGEXP '
+ " AND ",
+ " OR ",
+ "||",
+ " < ",
+ " <= ",
+ " > ",
+ " >= ",
+ " = ",
+ " <> ",
+ " IS ",
+ " IS NOT ",
+ " IN ",
+ " LIKE ",
+ " GLOB ",
+ " MATCH ",
+ " REGEXP ",
]
math_functions = [
# SQL math functions
- "Abs", "ACos", "ASin", "ATan", "Cos", "Cot", "Degrees", "Exp", "Floor", "Log", "Log2",
- "Log10", "Pi", "Radians", "Round", "Sign", "Sin", "Sqrt", "StdDev_Pop", "StdDev_Samp", "Tan",
- "Var_Pop", "Var_Samp"]
+ "Abs",
+ "ACos",
+ "ASin",
+ "ATan",
+ "Cos",
+ "Cot",
+ "Degrees",
+ "Exp",
+ "Floor",
+ "Log",
+ "Log2",
+ "Log10",
+ "Pi",
+ "Radians",
+ "Round",
+ "Sign",
+ "Sin",
+ "Sqrt",
+ "StdDev_Pop",
+ "StdDev_Samp",
+ "Tan",
+ "Var_Pop",
+ "Var_Samp",
+]
-string_functions = ["Length", "Lower", "Upper", "Like", "Trim", "LTrim", "RTrim", "Replace", "Substr"]
+string_functions = [
+ "Length",
+ "Lower",
+ "Upper",
+ "Like",
+ "Trim",
+ "LTrim",
+ "RTrim",
+ "Replace",
+ "Substr",
+]
aggregate_functions = [
- "Max", "Min", "Avg", "Count", "Sum", "Group_Concat", "Total", "Var_Pop", "Var_Samp", "StdDev_Pop", "StdDev_Samp"
+ "Max",
+ "Min",
+ "Avg",
+ "Count",
+ "Sum",
+ "Group_Concat",
+ "Total",
+ "Var_Pop",
+ "Var_Samp",
+ "StdDev_Pop",
+ "StdDev_Samp",
]
spatialite_functions = [ # from www.gaia-gis.it/spatialite-2.3.0/spatialite-sql-2.3.0.html
- # SQL utility functions for BLOB objects
- "*iszipblob", "*ispdfblob", "*isgifblob", "*ispngblob", "*isjpegblob", "*isexifblob",
- "*isexifgpsblob", "*geomfromexifgpsblob", "MakePoint", "BuildMbr", "*buildcirclembr", "ST_MinX",
- "ST_MinY", "ST_MaxX", "ST_MaxY",
- # SQL functions for constructing a geometric object given its Well-known Text Representation
- "ST_GeomFromText", "*pointfromtext",
- # SQL functions for constructing a geometric object given its Well-known Binary Representation
- "*geomfromwkb", "*pointfromwkb",
- # SQL functions for obtaining the Well-known Text / Well-known Binary Representation of a geometric object
- "ST_AsText", "ST_AsBinary",
- # SQL functions supporting exotic geometric formats
- "*assvg", "*asfgf", "*geomfromfgf",
- # SQL functions on type Geometry
- "ST_Dimension", "ST_GeometryType", "ST_Srid", "ST_SetSrid", "ST_isEmpty", "ST_isSimple", "ST_isValid", "ST_Boundary",
- "ST_Envelope",
- # SQL functions on type Point
- "ST_X", "ST_Y",
- # SQL functions on type Curve [Linestring or Ring]
- "ST_StartPoint", "ST_EndPoint", "ST_Length", "ST_isClosed", "ST_isRing", "ST_Simplify",
- "*simplifypreservetopology",
- # SQL functions on type LineString
- "ST_NumPoints", "ST_PointN",
- # SQL functions on type Surface [Polygon or Ring]
- "ST_Centroid", "ST_PointOnSurface", "ST_Area",
- # SQL functions on type Polygon
- "ST_ExteriorRing", "ST_InteriorRingN",
- # SQL functions on type GeomCollection
- "ST_NumGeometries", "ST_GeometryN",
- # SQL functions that test approximative spatial relationships via MBRs
- "MbrEqual", "MbrDisjoint", "MbrTouches", "MbrWithin", "MbrOverlaps", "MbrIntersects",
- "MbrContains",
- # SQL functions that test spatial relationships
- "ST_Equals", "ST_Disjoint", "ST_Touches", "ST_Within", "ST_Overlaps", "ST_Crosses", "ST_Intersects", "ST_Contains",
- "ST_Relate",
- # SQL functions for distance relationships
- "ST_Distance",
- # SQL functions that implement spatial operators
- "ST_Intersection", "ST_Difference", "ST_Union", "ST_SymDifference", "ST_Buffer", "ST_ConvexHull",
- # SQL functions for coordinate transformations
- "ST_Transform",
- # SQL functions for Spatial-MetaData and Spatial-Index handling
- "*initspatialmetadata", "*addgeometrycolumn", "*recovergeometrycolumn", "*discardgeometrycolumn",
- "*createspatialindex", "*creatembrcache", "*disablespatialindex",
- # SQL functions implementing FDO/OGR compatibility
- "*checkspatialmetadata", "*autofdostart", "*autofdostop", "*initfdospatialmetadata",
- "*addfdogeometrycolumn", "*recoverfdogeometrycolumn", "*discardfdogeometrycolumn",
- # SQL functions for MbrCache-based queries
- "*filtermbrwithin", "*filtermbrcontains", "*filtermbrintersects", "*buildmbrfilter"
+ # SQL utility functions for BLOB objects
+ "*iszipblob",
+ "*ispdfblob",
+ "*isgifblob",
+ "*ispngblob",
+ "*isjpegblob",
+ "*isexifblob",
+ "*isexifgpsblob",
+ "*geomfromexifgpsblob",
+ "MakePoint",
+ "BuildMbr",
+ "*buildcirclembr",
+ "ST_MinX",
+ "ST_MinY",
+ "ST_MaxX",
+ "ST_MaxY",
+ # SQL functions for constructing a geometric object given its Well-known Text Representation
+ "ST_GeomFromText",
+ "*pointfromtext",
+ # SQL functions for constructing a geometric object given its Well-known Binary Representation
+ "*geomfromwkb",
+ "*pointfromwkb",
+ # SQL functions for obtaining the Well-known Text / Well-known Binary Representation of a geometric object
+ "ST_AsText",
+ "ST_AsBinary",
+ # SQL functions supporting exotic geometric formats
+ "*assvg",
+ "*asfgf",
+ "*geomfromfgf",
+ # SQL functions on type Geometry
+ "ST_Dimension",
+ "ST_GeometryType",
+ "ST_Srid",
+ "ST_SetSrid",
+ "ST_isEmpty",
+ "ST_isSimple",
+ "ST_isValid",
+ "ST_Boundary",
+ "ST_Envelope",
+ # SQL functions on type Point
+ "ST_X",
+ "ST_Y",
+ # SQL functions on type Curve [Linestring or Ring]
+ "ST_StartPoint",
+ "ST_EndPoint",
+ "ST_Length",
+ "ST_isClosed",
+ "ST_isRing",
+ "ST_Simplify",
+ "*simplifypreservetopology",
+ # SQL functions on type LineString
+ "ST_NumPoints",
+ "ST_PointN",
+ # SQL functions on type Surface [Polygon or Ring]
+ "ST_Centroid",
+ "ST_PointOnSurface",
+ "ST_Area",
+ # SQL functions on type Polygon
+ "ST_ExteriorRing",
+ "ST_InteriorRingN",
+ # SQL functions on type GeomCollection
+ "ST_NumGeometries",
+ "ST_GeometryN",
+ # SQL functions that test approximative spatial relationships via MBRs
+ "MbrEqual",
+ "MbrDisjoint",
+ "MbrTouches",
+ "MbrWithin",
+ "MbrOverlaps",
+ "MbrIntersects",
+ "MbrContains",
+ # SQL functions that test spatial relationships
+ "ST_Equals",
+ "ST_Disjoint",
+ "ST_Touches",
+ "ST_Within",
+ "ST_Overlaps",
+ "ST_Crosses",
+ "ST_Intersects",
+ "ST_Contains",
+ "ST_Relate",
+ # SQL functions for distance relationships
+ "ST_Distance",
+ # SQL functions that implement spatial operators
+ "ST_Intersection",
+ "ST_Difference",
+ "ST_Union",
+ "ST_SymDifference",
+ "ST_Buffer",
+ "ST_ConvexHull",
+ # SQL functions for coordinate transformations
+ "ST_Transform",
+ # SQL functions for Spatial-MetaData and Spatial-Index handling
+ "*initspatialmetadata",
+ "*addgeometrycolumn",
+ "*recovergeometrycolumn",
+ "*discardgeometrycolumn",
+ "*createspatialindex",
+ "*creatembrcache",
+ "*disablespatialindex",
+ # SQL functions implementing FDO/OGR compatibility
+ "*checkspatialmetadata",
+ "*autofdostart",
+ "*autofdostop",
+ "*initfdospatialmetadata",
+ "*addfdogeometrycolumn",
+ "*recoverfdogeometrycolumn",
+ "*discardfdogeometrycolumn",
+ # SQL functions for MbrCache-based queries
+ "*filtermbrwithin",
+ "*filtermbrcontains",
+ "*filtermbrintersects",
+ "*buildmbrfilter",
]
qgis_functions = [
- "atan2", "round", "rand", "randf", "clamp", "scale_linear", "scale_polynomial", "scale_exponential", "_pi", "to_int", "toint", "to_real", "toreal",
- "to_string", "tostring", "to_datetime", "todatetime", "to_date", "todate", "to_time", "totime", "to_interval", "tointerval",
- "regexp_match", "now", "_now", "age", "year", "month", "week", "day", "hour", "minute", "second", "day_of_week", "title",
- "levenshtein", "longest_common_substring", "hamming_distance", "wordwrap", "regexp_replace", "regexp_substr", "concat",
- "strpos", "_left", "_right", "rpad", "lpad", "format", "format_number", "format_date", "color_rgb", "color_rgba", "color_rgbf", "ramp_color", "ramp_color_object",
- "color_hsl", "color_hsla", "color_hslf", "color_hsv", "color_hsva", "color_hsvf", "color_cmyk", "color_cmyka", "color_cmykf", "color_part", "darker", "lighter",
- "set_color_part", "point_n", "start_point", "end_point", "nodes_to_points", "segments_to_lines", "make_point",
- "make_point_m", "make_line", "make_polygon", "x_min", "xmin", "x_max", "xmax", "y_min", "ymin", "y_max", "ymax", "geom_from_wkt",
- "geomFromWKT", "geom_from_gml", "relate", "intersects_bbox", "bbox", "translate", "buffer", "point_on_surface", "reverse",
- "exterior_ring", "interior_ring_n", "geometry_n", "bounds", "num_points", "num_interior_rings", "num_rings", "num_geometries",
- "bounds_width", "bounds_height", "is_closed", "convex_hull", "sym_difference", "combine", "_union", "geom_to_wkt", "geomToWKT",
- "transform", "uuid", "_uuid", "layer_property", "var", "_specialcol_", "project_color", "project_color_object"]
+ "atan2",
+ "round",
+ "rand",
+ "randf",
+ "clamp",
+ "scale_linear",
+ "scale_polynomial",
+ "scale_exponential",
+ "_pi",
+ "to_int",
+ "toint",
+ "to_real",
+ "toreal",
+ "to_string",
+ "tostring",
+ "to_datetime",
+ "todatetime",
+ "to_date",
+ "todate",
+ "to_time",
+ "totime",
+ "to_interval",
+ "tointerval",
+ "regexp_match",
+ "now",
+ "_now",
+ "age",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "day_of_week",
+ "title",
+ "levenshtein",
+ "longest_common_substring",
+ "hamming_distance",
+ "wordwrap",
+ "regexp_replace",
+ "regexp_substr",
+ "concat",
+ "strpos",
+ "_left",
+ "_right",
+ "rpad",
+ "lpad",
+ "format",
+ "format_number",
+ "format_date",
+ "color_rgb",
+ "color_rgba",
+ "color_rgbf",
+ "ramp_color",
+ "ramp_color_object",
+ "color_hsl",
+ "color_hsla",
+ "color_hslf",
+ "color_hsv",
+ "color_hsva",
+ "color_hsvf",
+ "color_cmyk",
+ "color_cmyka",
+ "color_cmykf",
+ "color_part",
+ "darker",
+ "lighter",
+ "set_color_part",
+ "point_n",
+ "start_point",
+ "end_point",
+ "nodes_to_points",
+ "segments_to_lines",
+ "make_point",
+ "make_point_m",
+ "make_line",
+ "make_polygon",
+ "x_min",
+ "xmin",
+ "x_max",
+ "xmax",
+ "y_min",
+ "ymin",
+ "y_max",
+ "ymax",
+ "geom_from_wkt",
+ "geomFromWKT",
+ "geom_from_gml",
+ "relate",
+ "intersects_bbox",
+ "bbox",
+ "translate",
+ "buffer",
+ "point_on_surface",
+ "reverse",
+ "exterior_ring",
+ "interior_ring_n",
+ "geometry_n",
+ "bounds",
+ "num_points",
+ "num_interior_rings",
+ "num_rings",
+ "num_geometries",
+ "bounds_width",
+ "bounds_height",
+ "is_closed",
+ "convex_hull",
+ "sym_difference",
+ "combine",
+ "_union",
+ "geom_to_wkt",
+ "geomToWKT",
+ "transform",
+ "uuid",
+ "_uuid",
+ "layer_property",
+ "var",
+ "_specialcol_",
+ "project_color",
+ "project_color_object",
+]
# constants
@@ -140,7 +490,7 @@
def getSqlDictionary(spatial=True):
def strip_star(s):
- if s[0] == '*':
+ if s[0] == "*":
return s.lower()[1:]
else:
return s.lower()
@@ -153,20 +503,34 @@ def strip_star(s):
f += qgis_functions
c += spatialite_constants
- return {'keyword': list(map(strip_star, k)), 'constant': list(map(strip_star, c)), 'function': list(map(strip_star, f))}
+ return {
+ "keyword": list(map(strip_star, k)),
+ "constant": list(map(strip_star, c)),
+ "function": list(map(strip_star, f)),
+ }
def getQueryBuilderDictionary():
# concat functions
def ff(l):
- return [s for s in l if s[0] != '*']
+ return [s for s in l if s[0] != "*"]
def add_paren(l):
return [s + "(" for s in l]
- foo = sorted(add_paren(ff(list(set.union(set(functions), set(spatialite_functions), set(qgis_functions))))))
+ foo = sorted(
+ add_paren(
+ ff(
+ list(
+ set.union(
+ set(functions), set(spatialite_functions), set(qgis_functions)
+ )
+ )
+ )
+ )
+ )
m = sorted(add_paren(ff(math_functions)))
agg = sorted(add_paren(ff(aggregate_functions)))
op = ff(operators)
s = sorted(add_paren(ff(string_functions)))
- return {'function': foo, 'math': m, 'aggregate': agg, 'operator': op, 'string': s}
+ return {"function": foo, "math": m, "aggregate": agg, "operator": op, "string": s}
diff --git a/python/plugins/db_manager/db_tree.py b/python/plugins/db_manager/db_tree.py
index c40d2f42f2bf..2a080cbdaecd 100644
--- a/python/plugins/db_manager/db_tree.py
+++ b/python/plugins/db_manager/db_tree.py
@@ -41,7 +41,9 @@ def __init__(self, mainWindow):
self.setModel(DBModel(self))
self.setHeaderHidden(True)
- self.setEditTriggers(QTreeView.EditTrigger.EditKeyPressed | QTreeView.EditTrigger.SelectedClicked)
+ self.setEditTriggers(
+ QTreeView.EditTrigger.EditKeyPressed | QTreeView.EditTrigger.SelectedClicked
+ )
self.setDragEnabled(True)
self.setAcceptDrops(True)
@@ -125,8 +127,12 @@ def contextMenuEvent(self, ev):
menu = QMenu(self)
if isinstance(item, (Table, Schema)) and not isinstance(item, LTable):
- if not (isinstance(item, GPKGRasterTable) and int(gdal.VersionInfo()) < 3100000):
- menu.addAction(QCoreApplication.translate("DBTree", "Rename…"), self.rename)
+ if not (
+ isinstance(item, GPKGRasterTable) and int(gdal.VersionInfo()) < 3100000
+ ):
+ menu.addAction(
+ QCoreApplication.translate("DBTree", "Rename…"), self.rename
+ )
menu.addAction(QCoreApplication.translate("DBTree", "Delete…"), self.delete)
if isinstance(item, Table) and item.canBeAddedToCanvas():
@@ -140,7 +146,10 @@ def contextMenuEvent(self, ev):
menu.addAction(self.tr("Remove"), self.delete)
elif not index.parent().isValid() and item.typeName() in ("spatialite", "gpkg"):
- menu.addAction(QCoreApplication.translate("DBTree", "New Connection…"), self.newConnection)
+ menu.addAction(
+ QCoreApplication.translate("DBTree", "New Connection…"),
+ self.newConnection,
+ )
if not menu.isEmpty():
menu.exec(ev.globalPos())
@@ -166,14 +175,28 @@ def addLayer(self):
layers = QgsProject.instance().addMapLayers([layer])
if len(layers) != 1:
QgsMessageLog.logMessage(
- self.tr("%1 is an invalid layer - not loaded").replace("%1", layer.publicSource()))
- msgLabel = QLabel(self.tr(
- "%1 is an invalid layer and cannot be loaded. Please check the message log for further info.").replace(
- "%1", layer.publicSource()), self.mainWindow.infoBar)
+ self.tr("%1 is an invalid layer - not loaded").replace(
+ "%1", layer.publicSource()
+ )
+ )
+ msgLabel = QLabel(
+ self.tr(
+ '%1 is an invalid layer and cannot be loaded. Please check the message log for further info.'
+ ).replace("%1", layer.publicSource()),
+ self.mainWindow.infoBar,
+ )
msgLabel.setWordWrap(True)
- msgLabel.linkActivated.connect(self.mainWindow.iface.mainWindow().findChild(QWidget, "MessageLog").show)
- msgLabel.linkActivated.connect(self.mainWindow.iface.mainWindow().raise_)
- self.mainWindow.infoBar.pushItem(QgsMessageBarItem(msgLabel, Qgis.MessageLevel.Warning))
+ msgLabel.linkActivated.connect(
+ self.mainWindow.iface.mainWindow()
+ .findChild(QWidget, "MessageLog")
+ .show
+ )
+ msgLabel.linkActivated.connect(
+ self.mainWindow.iface.mainWindow().raise_
+ )
+ self.mainWindow.infoBar.pushItem(
+ QgsMessageBarItem(msgLabel, Qgis.MessageLevel.Warning)
+ )
def reconnect(self):
db = self.currentDatabase()
diff --git a/python/plugins/db_manager/dlg_add_geometry_column.py b/python/plugins/db_manager/dlg_add_geometry_column.py
index 559fae0ca76d..3a22e86a16d8 100644
--- a/python/plugins/db_manager/dlg_add_geometry_column.py
+++ b/python/plugins/db_manager/dlg_add_geometry_column.py
@@ -29,12 +29,19 @@
from .dlg_db_error import DlgDbError
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgAddGeometryColumn.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgAddGeometryColumn.ui"))
class DlgAddGeometryColumn(QDialog, Ui_Dialog):
- GEOM_TYPES = ["POINT", "LINESTRING", "POLYGON", "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
- "GEOMETRYCOLLECTION"]
+ GEOM_TYPES = [
+ "POINT",
+ "LINESTRING",
+ "POLYGON",
+ "MULTIPOINT",
+ "MULTILINESTRING",
+ "MULTIPOLYGON",
+ "GEOMETRYCOLLECTION",
+ ]
def __init__(self, parent=None, table=None, db=None):
QDialog.__init__(self, parent)
@@ -45,9 +52,11 @@ def __init__(self, parent=None, table=None, db=None):
self.buttonBox.accepted.connect(self.createGeomColumn)
def createGeomColumn(self):
- """ first check whether everything's fine """
+ """first check whether everything's fine"""
if self.editName.text() == "":
- QMessageBox.critical(self, self.tr("DB Manager"), self.tr("Field name must not be empty."))
+ QMessageBox.critical(
+ self, self.tr("DB Manager"), self.tr("Field name must not be empty.")
+ )
return
name = self.editName.text()
@@ -62,7 +71,9 @@ def createGeomColumn(self):
# now create the geometry column
with OverrideCursor(Qt.CursorShape.WaitCursor):
try:
- self.table.addGeometryColumn(name, geom_type, srid, dim, createSpatialIndex)
+ self.table.addGeometryColumn(
+ name, geom_type, srid, dim, createSpatialIndex
+ )
except DbError as e:
DlgDbError.showError(e, self)
return
diff --git a/python/plugins/db_manager/dlg_create_constraint.py b/python/plugins/db_manager/dlg_create_constraint.py
index b2c571494fec..aeb0e62e163e 100644
--- a/python/plugins/db_manager/dlg_create_constraint.py
+++ b/python/plugins/db_manager/dlg_create_constraint.py
@@ -30,7 +30,7 @@
from .db_plugins.plugin import TableConstraint
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgCreateConstraint.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgCreateConstraint.ui"))
class DlgCreateConstraint(QDialog, Ui_Dialog):
@@ -65,7 +65,11 @@ def createConstraint(self):
def getConstraint(self):
constr = TableConstraint(self.table)
constr.name = ""
- constr.type = TableConstraint.TypePrimaryKey if self.radPrimaryKey.isChecked() else TableConstraint.TypeUnique
+ constr.type = (
+ TableConstraint.TypePrimaryKey
+ if self.radPrimaryKey.isChecked()
+ else TableConstraint.TypeUnique
+ )
constr.columns = []
column = self.cboColumn.currentText()
for fld in self.table.fields():
diff --git a/python/plugins/db_manager/dlg_create_index.py b/python/plugins/db_manager/dlg_create_index.py
index 40e3b4c1611a..2a6452edeee2 100644
--- a/python/plugins/db_manager/dlg_create_index.py
+++ b/python/plugins/db_manager/dlg_create_index.py
@@ -30,7 +30,7 @@
from .db_plugins.plugin import TableIndex
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgCreateIndex.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgCreateIndex.ui"))
class DlgCreateIndex(QDialog, Ui_Dialog):
@@ -52,12 +52,14 @@ def populateColumns(self):
self.cboColumn.addItem(fld.name)
def columnChanged(self):
- self.editName.setText("idx_%s_%s" % (self.table.name, self.cboColumn.currentText()))
+ self.editName.setText(f"idx_{self.table.name}_{self.cboColumn.currentText()}")
def createIndex(self):
idx = self.getIndex()
if idx.name == "":
- QMessageBox.critical(self, self.tr("Error"), self.tr("Please enter a name for the index."))
+ QMessageBox.critical(
+ self, self.tr("Error"), self.tr("Please enter a name for the index.")
+ )
return
# now create the index
diff --git a/python/plugins/db_manager/dlg_create_table.py b/python/plugins/db_manager/dlg_create_table.py
index c7b36d6e6d4d..7e617a8a3707 100644
--- a/python/plugins/db_manager/dlg_create_table.py
+++ b/python/plugins/db_manager/dlg_create_table.py
@@ -22,7 +22,15 @@
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, QModelIndex
-from qgis.PyQt.QtWidgets import QItemDelegate, QComboBox, QDialog, QPushButton, QDialogButtonBox, QMessageBox, QApplication
+from qgis.PyQt.QtWidgets import (
+ QItemDelegate,
+ QComboBox,
+ QDialog,
+ QPushButton,
+ QDialogButtonBox,
+ QMessageBox,
+ QApplication,
+)
from qgis.PyQt.QtCore import QItemSelectionModel, pyqtSignal
from qgis.utils import OverrideCursor
@@ -32,11 +40,11 @@
from .dlg_db_error import DlgDbError
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgCreateTable.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgCreateTable.ui"))
class TableFieldsDelegate(QItemDelegate):
- """ delegate with some special item editors """
+ """delegate with some special item editors"""
columnNameChanged = pyqtSignal()
@@ -56,7 +64,7 @@ def createEditor(self, parent, option, index):
return QItemDelegate.createEditor(self, parent, option, index)
def setEditorData(self, editor, index):
- """ load data from model to editor """
+ """load data from model to editor"""
m = index.model()
if index.column() == 1:
txt = m.data(index, Qt.ItemDataRole.DisplayRole)
@@ -66,7 +74,7 @@ def setEditorData(self, editor, index):
QItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
- """ save data from editor back to model """
+ """save data from editor back to model"""
if index.column() == 1:
model.setData(index, editor.currentText())
else:
@@ -77,8 +85,15 @@ def setModelData(self, editor, model, index):
class DlgCreateTable(QDialog, Ui_Dialog):
- GEOM_TYPES = ["POINT", "LINESTRING", "POLYGON", "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
- "GEOMETRYCOLLECTION"]
+ GEOM_TYPES = [
+ "POINT",
+ "LINESTRING",
+ "POLYGON",
+ "MULTIPOINT",
+ "MULTILINESTRING",
+ "MULTIPOLYGON",
+ "GEOMETRYCOLLECTION",
+ ]
def __init__(self, item, parent=None):
QDialog.__init__(self, parent)
@@ -128,7 +143,7 @@ def populateSchemas(self):
index = -1
for schema in self.schemas:
self.cboSchema.addItem(schema.name)
- if hasattr(self.item, 'schema') and schema.name == self.item.schema().name:
+ if hasattr(self.item, "schema") and schema.name == self.item.schema().name:
index = self.cboSchema.count() - 1
self.cboSchema.setCurrentIndex(index)
@@ -146,8 +161,8 @@ def updateUi(self):
def updateUiFields(self):
fld = self.selectedField()
if fld is not None:
- up_enabled = (fld != 0)
- down_enabled = (fld != self.fields.model().rowCount() - 1)
+ up_enabled = fld != 0
+ down_enabled = fld != self.fields.model().rowCount() - 1
del_enabled = True
else:
up_enabled, down_enabled, del_enabled = False, False, False
@@ -156,7 +171,7 @@ def updateUiFields(self):
self.btnDeleteField.setEnabled(del_enabled)
def updatePkeyCombo(self, selRow=None):
- """ called when list of columns changes. if 'sel' is None, it keeps current index """
+ """called when list of columns changes. if 'sel' is None, it keeps current index"""
if selRow is None:
selRow = self.cboPrimaryKey.currentIndex()
@@ -171,7 +186,7 @@ def updatePkeyCombo(self, selRow=None):
self.cboPrimaryKey.setCurrentIndex(selRow)
def addField(self):
- """Adds new field to the end of field table """
+ """Adds new field to the end of field table"""
m = self.fields.model()
newRow = m.rowCount()
m.insertRows(newRow, 1)
@@ -192,7 +207,11 @@ def addField(self):
# selects the new row
sel = self.fields.selectionModel()
- sel.select(indexName, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
+ sel.select(
+ indexName,
+ QItemSelectionModel.SelectionFlag.Rows
+ | QItemSelectionModel.SelectionFlag.ClearAndSelect,
+ )
# starts editing
self.fields.edit(indexName)
@@ -206,23 +225,29 @@ def selectedField(self):
return sel[0].row()
def deleteField(self):
- """Deletes selected field """
+ """Deletes selected field"""
row = self.selectedField()
if row is None:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No field selected.")
+ )
else:
self.fields.model().removeRows(row, 1)
self.updatePkeyCombo()
def fieldUp(self):
- """ move selected field up """
+ """move selected field up"""
row = self.selectedField()
if row is None:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No field selected.")
+ )
return
if row == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("Field is already at the top."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("Field is already at the top.")
+ )
return
# take row and reinsert it
@@ -231,18 +256,26 @@ def fieldUp(self):
# set selection again
index = self.fields.model().index(row - 1, 0, QModelIndex())
- self.fields.selectionModel().select(index, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
+ self.fields.selectionModel().select(
+ index,
+ QItemSelectionModel.SelectionFlag.Rows
+ | QItemSelectionModel.SelectionFlag.ClearAndSelect,
+ )
self.updatePkeyCombo()
def fieldDown(self):
- """ move selected field down """
+ """move selected field down"""
row = self.selectedField()
if row is None:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No field selected.")
+ )
return
if row == self.fields.model().rowCount() - 1:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("Field is already at the bottom."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("Field is already at the bottom.")
+ )
return
# take row and reinsert it
@@ -251,35 +284,51 @@ def fieldDown(self):
# set selection again
index = self.fields.model().index(row + 1, 0, QModelIndex())
- self.fields.selectionModel().select(index, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
+ self.fields.selectionModel().select(
+ index,
+ QItemSelectionModel.SelectionFlag.Rows
+ | QItemSelectionModel.SelectionFlag.ClearAndSelect,
+ )
self.updatePkeyCombo()
def createTable(self):
- """Creates table with chosen fields, optionally add a geometry column """
+ """Creates table with chosen fields, optionally add a geometry column"""
if not self.hasSchemas:
schema = None
else:
schema = str(self.cboSchema.currentText())
if len(schema) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("A valid schema must be selected first."))
+ QMessageBox.information(
+ self,
+ self.tr("DB Manager"),
+ self.tr("A valid schema must be selected first."),
+ )
return
table = str(self.editName.text())
if len(table) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("A valid table name is required."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("A valid table name is required.")
+ )
return
m = self.fields.model()
if m.rowCount() == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("At least one field is required."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("At least one field is required.")
+ )
return
useGeomColumn = self.chkGeomColumn.isChecked()
if useGeomColumn:
geomColumn = str(self.editGeomColumn.text())
if len(geomColumn) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("A name is required for the geometry column."))
+ QMessageBox.information(
+ self,
+ self.tr("DB Manager"),
+ self.tr("A name is required for the geometry column."),
+ )
return
geomType = self.GEOM_TYPES[self.cboGeomType.currentIndex()]
@@ -321,4 +370,6 @@ def createTable(self):
self.editGeomSrid.setEnabled(False)
self.chkSpatialIndex.setEnabled(False)
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("Table created successfully."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("Table created successfully.")
+ )
diff --git a/python/plugins/db_manager/dlg_db_error.py b/python/plugins/db_manager/dlg_db_error.py
index 3da3daa55cda..87cef96f0012 100644
--- a/python/plugins/db_manager/dlg_db_error.py
+++ b/python/plugins/db_manager/dlg_db_error.py
@@ -26,7 +26,7 @@
from .gui_utils import GuiUtils
from .db_plugins.plugin import DbError
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgDbError.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgDbError.ui"))
class DlgDbError(QDialog, Ui_Dialog):
@@ -36,7 +36,7 @@ def __init__(self, e, parent=None):
self.setupUi(self)
def sanitize(txt):
- return "" if txt is None else "
" + txt.replace('<', '<') + "
"
+ return "" if txt is None else "
" + txt.replace("<", "<") + "
"
if isinstance(e, DbError):
self.setQueryMessage(sanitize(e.msg), sanitize(e.query))
diff --git a/python/plugins/db_manager/dlg_export_vector.py b/python/plugins/db_manager/dlg_export_vector.py
index 232b821e3d0d..26282c364134 100644
--- a/python/plugins/db_manager/dlg_export_vector.py
+++ b/python/plugins/db_manager/dlg_export_vector.py
@@ -25,16 +25,18 @@
from qgis.PyQt.QtWidgets import QDialog, QFileDialog, QMessageBox, QApplication
from qgis.PyQt.QtGui import QCursor
-from qgis.core import (QgsVectorFileWriter,
- QgsVectorDataProvider,
- QgsCoordinateReferenceSystem,
- QgsVectorLayerExporter,
- QgsSettings)
+from qgis.core import (
+ QgsVectorFileWriter,
+ QgsVectorDataProvider,
+ QgsCoordinateReferenceSystem,
+ QgsVectorLayerExporter,
+ QgsSettings,
+)
from qgis.utils import OverrideCursor
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgExportVector.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgExportVector.ui"))
class DlgExportVector(QDialog, Ui_Dialog):
@@ -46,8 +48,8 @@ def __init__(self, inLayer, inDb, parent=None):
self.setupUi(self)
vectorFilterName = "lastVectorFileFilter" # "lastRasterFileFilter"
- self.lastUsedVectorFilterSettingsKey = "/UI/{}".format(vectorFilterName)
- self.lastUsedVectorDirSettingsKey = "/UI/{}Dir".format(vectorFilterName)
+ self.lastUsedVectorFilterSettingsKey = f"/UI/{vectorFilterName}"
+ self.lastUsedVectorDirSettingsKey = f"/UI/{vectorFilterName}Dir"
# update UI
self.setupWorkingMode()
@@ -65,7 +67,7 @@ def setupWorkingMode(self):
self.checkSupports()
def checkSupports(self):
- """ update options available for the current input layer """
+ """update options available for the current input layer"""
allowSpatial = self.db.connector.hasSpatialSupport()
hasGeomType = self.inLayer and self.inLayer.isSpatial()
self.chkSourceSrid.setEnabled(allowSpatial and hasGeomType)
@@ -82,19 +84,22 @@ def chooseOutputFile(self):
selected_filter = QgsVectorFileWriter.filterForDriver(selected_driver)
# ask for a filename
- filename, filter = QFileDialog.getSaveFileName(self, self.tr("Choose where to save the file"), lastUsedDir,
- selected_filter)
+ filename, filter = QFileDialog.getSaveFileName(
+ self, self.tr("Choose where to save the file"), lastUsedDir, selected_filter
+ )
if filename == "":
return
- ext = selected_filter[selected_filter.find('.'):]
- ext = ext[:ext.find(' ')]
+ ext = selected_filter[selected_filter.find(".") :]
+ ext = ext[: ext.find(" ")]
if not filename.lower().endswith(ext):
filename += ext
# store the last used dir
- settings.setValue(self.lastUsedVectorDirSettingsKey, QFileInfo(filename).filePath())
+ settings.setValue(
+ self.lastUsedVectorDirSettingsKey, QFileInfo(filename).filePath()
+ )
self.editOutputFile.setText(filename)
@@ -127,23 +132,31 @@ def populateFileFilters(self):
def accept(self):
# sanity checks
if self.editOutputFile.text() == "":
- QMessageBox.information(self, self.tr("Export to file"), self.tr("Output file name is required"))
+ QMessageBox.information(
+ self, self.tr("Export to file"), self.tr("Output file name is required")
+ )
return
if self.chkSourceSrid.isEnabled() and self.chkSourceSrid.isChecked():
try:
sourceSrid = int(self.editSourceSrid.text())
except ValueError:
- QMessageBox.information(self, self.tr("Export to file"),
- self.tr("Invalid source srid: must be an integer"))
+ QMessageBox.information(
+ self,
+ self.tr("Export to file"),
+ self.tr("Invalid source srid: must be an integer"),
+ )
return
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
try:
targetSrid = int(self.editTargetSrid.text())
except ValueError:
- QMessageBox.information(self, self.tr("Export to file"),
- self.tr("Invalid target srid: must be an integer"))
+ QMessageBox.information(
+ self,
+ self.tr("Export to file"),
+ self.tr("Invalid target srid: must be an integer"),
+ )
return
with OverrideCursor(Qt.CursorShape.WaitCursor):
@@ -157,15 +170,15 @@ def accept(self):
# set the OGR driver will be used
driverName = self.cboFileFormat.currentData()
- options['driverName'] = driverName
+ options["driverName"] = driverName
# set the output file encoding
if self.chkEncoding.isEnabled() and self.chkEncoding.isChecked():
enc = self.cboEncoding.currentText()
- options['fileEncoding'] = enc
+ options["fileEncoding"] = enc
if self.chkDropTable.isChecked():
- options['overwrite'] = True
+ options["overwrite"] = True
outCrs = QgsCoordinateReferenceSystem()
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
@@ -179,8 +192,9 @@ def accept(self):
self.inLayer.setCrs(inCrs)
# do the export!
- ret, errMsg = QgsVectorLayerExporter.exportLayer(self.inLayer, uri, providerName, outCrs,
- False, options)
+ ret, errMsg = QgsVectorLayerExporter.exportLayer(
+ self.inLayer, uri, providerName, outCrs, False, options
+ )
except Exception as e:
ret = -1
errMsg = str(e)
@@ -190,12 +204,18 @@ def accept(self):
self.inLayer.setCrs(prevInCrs)
if ret != 0:
- QMessageBox.warning(self, self.tr("Export to file"), self.tr("Error {0}\n{1}").format(ret, errMsg))
+ QMessageBox.warning(
+ self,
+ self.tr("Export to file"),
+ self.tr("Error {0}\n{1}").format(ret, errMsg),
+ )
return
# create spatial index
# if self.chkSpatialIndex.isEnabled() and self.chkSpatialIndex.isChecked():
# self.db.connector.createSpatialIndex( (schema, table), geom )
- QMessageBox.information(self, self.tr("Export to file"), self.tr("Export finished."))
+ QMessageBox.information(
+ self, self.tr("Export to file"), self.tr("Export finished.")
+ )
return QDialog.accept(self)
diff --git a/python/plugins/db_manager/dlg_field_properties.py b/python/plugins/db_manager/dlg_field_properties.py
index c4fb29b5c356..1548569605e9 100644
--- a/python/plugins/db_manager/dlg_field_properties.py
+++ b/python/plugins/db_manager/dlg_field_properties.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Giuseppe Sucameli'
-__date__ = 'April 2012'
-__copyright__ = '(C) 2012, Giuseppe Sucameli'
+__author__ = "Giuseppe Sucameli"
+__date__ = "April 2012"
+__copyright__ = "(C) 2012, Giuseppe Sucameli"
from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QDialog, QMessageBox
@@ -25,7 +25,7 @@
from .db_plugins.plugin import TableField
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgFieldProperties.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgFieldProperties.ui"))
class DlgFieldProperties(QDialog, Ui_Dialog):
@@ -81,13 +81,17 @@ def getField(self, newCopy=False):
return fld
def onOK(self):
- """ first check whether everything's fine """
+ """first check whether everything's fine"""
fld = self.getField(True) # don't change the original copy
if fld.name == "":
- QMessageBox.critical(self, self.tr("DB Manager"), self.tr("Field name must not be empty."))
+ QMessageBox.critical(
+ self, self.tr("DB Manager"), self.tr("Field name must not be empty.")
+ )
return
if fld.dataType == "":
- QMessageBox.critical(self, self.tr("DB Manager"), self.tr("Field type must not be empty."))
+ QMessageBox.critical(
+ self, self.tr("DB Manager"), self.tr("Field type must not be empty.")
+ )
return
self.accept()
diff --git a/python/plugins/db_manager/dlg_import_vector.py b/python/plugins/db_manager/dlg_import_vector.py
index ebc2140a468c..d0cdbf0552f6 100644
--- a/python/plugins/db_manager/dlg_import_vector.py
+++ b/python/plugins/db_manager/dlg_import_vector.py
@@ -24,21 +24,23 @@
from qgis.PyQt.QtCore import Qt, QFileInfo
from qgis.PyQt.QtWidgets import QDialog, QFileDialog, QMessageBox
-from qgis.core import (QgsDataSourceUri,
- QgsVectorDataProvider,
- QgsVectorLayer,
- QgsMapLayerType,
- QgsProviderRegistry,
- QgsCoordinateReferenceSystem,
- QgsVectorLayerExporter,
- QgsProject,
- QgsSettings)
+from qgis.core import (
+ QgsDataSourceUri,
+ QgsVectorDataProvider,
+ QgsVectorLayer,
+ QgsMapLayerType,
+ QgsProviderRegistry,
+ QgsCoordinateReferenceSystem,
+ QgsVectorLayerExporter,
+ QgsProject,
+ QgsSettings,
+)
from qgis.gui import QgsMessageViewer
from qgis.utils import OverrideCursor, iface
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgImportVector.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgImportVector.ui"))
class DlgImportVector(QDialog, Ui_Dialog):
@@ -59,7 +61,9 @@ def __init__(self, inLayer, outDb, outUri, parent=None):
self.default_pk = "id"
self.default_geom = "geom"
- self.mode = self.ASK_FOR_INPUT_MODE if self.inLayer is None else self.HAS_INPUT_MODE
+ self.mode = (
+ self.ASK_FOR_INPUT_MODE if self.inLayer is None else self.HAS_INPUT_MODE
+ )
# used to delete the inlayer whether created inside this dialog
self.inLayerMustBeDestroyed = False
@@ -77,7 +81,7 @@ def __init__(self, inLayer, outDb, outUri, parent=None):
self.updateInputLayer()
def setupWorkingMode(self, mode):
- """ hide the widget to select a layer/file if the input layer is already set """
+ """hide the widget to select a layer/file if the input layer is already set"""
self.wdgInput.setVisible(mode == self.ASK_FOR_INPUT_MODE)
self.resize(450, 350)
@@ -90,7 +94,9 @@ def setupWorkingMode(self, mode):
self.editPrimaryKey.setText(self.default_pk)
self.editGeomColumn.setText(self.default_geom)
- self.chkLowercaseFieldNames.setEnabled(self.db.hasLowercaseFieldNamesOption())
+ self.chkLowercaseFieldNames.setEnabled(
+ self.db.hasLowercaseFieldNamesOption()
+ )
if not self.chkLowercaseFieldNames.isEnabled():
self.chkLowercaseFieldNames.setChecked(False)
else:
@@ -99,10 +105,14 @@ def setupWorkingMode(self, mode):
self.updateInputLayer()
def checkSupports(self):
- """ update options available for the current input layer """
+ """update options available for the current input layer"""
allowSpatial = self.db.connector.hasSpatialSupport()
hasGeomType = self.inLayer and self.inLayer.isSpatial()
- isShapefile = self.inLayer and self.inLayer.providerType() == "ogr" and self.inLayer.storageType() == "ESRI Shapefile"
+ isShapefile = (
+ self.inLayer
+ and self.inLayer.providerType() == "ogr"
+ and self.inLayer.storageType() == "ESRI Shapefile"
+ )
self.chkGeomColumn.setEnabled(allowSpatial and hasGeomType)
if not self.chkGeomColumn.isEnabled():
@@ -142,7 +152,7 @@ def populateLayers(self):
self.cboInputLayer.setCurrentIndex(index)
def deleteInputLayer(self):
- """ unset the input layer, then destroy it but only if it was created from this dialog """
+ """unset the input layer, then destroy it but only if it was created from this dialog"""
if self.mode == self.ASK_FOR_INPUT_MODE and self.inLayer:
if self.inLayerMustBeDestroyed:
self.inLayer.deleteLater()
@@ -158,8 +168,13 @@ def chooseInputFile(self):
lastDir = settings.value("/db_manager/lastUsedDir", "")
lastVectorFormat = settings.value("/UI/lastVectorFileFilter", "")
# ask for a filename
- filename, lastVectorFormat = QFileDialog.getOpenFileName(self, self.tr("Choose the file to import"),
- lastDir, vectorFormats, lastVectorFormat)
+ filename, lastVectorFormat = QFileDialog.getOpenFileName(
+ self,
+ self.tr("Choose the file to import"),
+ lastDir,
+ vectorFormats,
+ lastVectorFormat,
+ )
if filename == "":
return
# store the last used dir and format
@@ -170,7 +185,7 @@ def chooseInputFile(self):
self.cboInputLayer.setEditText(filename)
def reloadInputLayer(self):
- """Creates the input layer and update available options """
+ """Creates the input layer and update available options"""
if self.mode != self.ASK_FOR_INPUT_MODE:
return True
@@ -264,7 +279,7 @@ def populateEncodings(self):
# populate the combo with supported encodings
self.cboEncoding.addItems(QgsVectorDataProvider.availableEncodings())
- self.cboEncoding.insertItem(0, self.tr('Automatic'), "")
+ self.cboEncoding.insertItem(0, self.tr("Automatic"), "")
self.cboEncoding.setCurrentIndex(0)
def accept(self):
@@ -275,23 +290,37 @@ def accept(self):
# sanity checks
if self.inLayer is None:
- QMessageBox.critical(self, self.tr("Import to Database"), self.tr("Input layer missing or not valid."))
+ QMessageBox.critical(
+ self,
+ self.tr("Import to Database"),
+ self.tr("Input layer missing or not valid."),
+ )
return
if self.cboTable.currentText() == "":
- QMessageBox.critical(self, self.tr("Import to Database"), self.tr("Output table name is required."))
+ QMessageBox.critical(
+ self,
+ self.tr("Import to Database"),
+ self.tr("Output table name is required."),
+ )
return
if self.chkSourceSrid.isEnabled() and self.chkSourceSrid.isChecked():
if not self.widgetSourceSrid.crs().isValid():
- QMessageBox.critical(self, self.tr("Import to Database"),
- self.tr("Invalid source srid: must be a valid crs."))
+ QMessageBox.critical(
+ self,
+ self.tr("Import to Database"),
+ self.tr("Invalid source srid: must be a valid crs."),
+ )
return
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
if not self.widgetTargetSrid.crs().isValid():
- QMessageBox.critical(self, self.tr("Import to Database"),
- self.tr("Invalid target srid: must be a valid crs."))
+ QMessageBox.critical(
+ self,
+ self.tr("Import to Database"),
+ self.tr("Invalid target srid: must be a valid crs."),
+ )
return
with OverrideCursor(Qt.CursorShape.WaitCursor):
@@ -300,48 +329,63 @@ def accept(self):
prevInEncoding = self.inLayer.dataProvider().encoding()
try:
- schema = self.outUri.schema() if not self.cboSchema.isEnabled() else self.cboSchema.currentText()
+ schema = (
+ self.outUri.schema()
+ if not self.cboSchema.isEnabled()
+ else self.cboSchema.currentText()
+ )
table = self.cboTable.currentText()
# get pk and geom field names from the source layer or use the
# ones defined by the user
srcUri = QgsDataSourceUri(self.inLayer.source())
- pk = srcUri.keyColumn() if not self.chkPrimaryKey.isChecked() else self.editPrimaryKey.text()
+ pk = (
+ srcUri.keyColumn()
+ if not self.chkPrimaryKey.isChecked()
+ else self.editPrimaryKey.text()
+ )
if not pk:
pk = self.default_pk
if self.inLayer.isSpatial() and self.chkGeomColumn.isEnabled():
- geom = srcUri.geometryColumn() if not self.chkGeomColumn.isChecked() else self.editGeomColumn.text()
+ geom = (
+ srcUri.geometryColumn()
+ if not self.chkGeomColumn.isChecked()
+ else self.editGeomColumn.text()
+ )
if not geom:
geom = self.default_geom
else:
geom = None
options = {}
- if self.chkLowercaseFieldNames.isEnabled() and self.chkLowercaseFieldNames.isChecked():
+ if (
+ self.chkLowercaseFieldNames.isEnabled()
+ and self.chkLowercaseFieldNames.isChecked()
+ ):
pk = pk.lower()
if geom:
geom = geom.lower()
- options['lowercaseFieldNames'] = True
+ options["lowercaseFieldNames"] = True
# get output params, update output URI
self.outUri.setDataSource(schema, table, geom, "", pk)
typeName = self.db.dbplugin().typeName()
providerName = self.db.dbplugin().providerName()
- if typeName == 'gpkg':
+ if typeName == "gpkg":
uri = self.outUri.database()
- options['update'] = True
- options['driverName'] = 'GPKG'
- options['layerName'] = table
+ options["update"] = True
+ options["driverName"] = "GPKG"
+ options["layerName"] = table
else:
uri = self.outUri.uri(False)
if self.chkDropTable.isChecked():
- options['overwrite'] = True
+ options["overwrite"] = True
if self.chkSinglePart.isEnabled() and self.chkSinglePart.isChecked():
- options['forceSinglePartGeometryType'] = True
+ options["forceSinglePartGeometryType"] = True
outCrs = QgsCoordinateReferenceSystem()
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
@@ -352,14 +396,20 @@ def accept(self):
inCrs = self.widgetSourceSrid.crs()
self.inLayer.setCrs(inCrs)
- if self.chkEncoding.isEnabled() and self.chkEncoding.isChecked() and self.cboEncoding.currentData() is None:
+ if (
+ self.chkEncoding.isEnabled()
+ and self.chkEncoding.isChecked()
+ and self.cboEncoding.currentData() is None
+ ):
enc = self.cboEncoding.currentText()
self.inLayer.setProviderEncoding(enc)
onlySelected = self.chkSelectedFeatures.isChecked()
# do the import!
- ret, errMsg = QgsVectorLayerExporter.exportLayer(self.inLayer, uri, providerName, outCrs, onlySelected, options)
+ ret, errMsg = QgsVectorLayerExporter.exportLayer(
+ self.inLayer, uri, providerName, outCrs, onlySelected, options
+ )
except Exception as e:
ret = -1
errMsg = str(e)
@@ -389,7 +439,9 @@ def accept(self):
self.db.connection().reconnect()
self.db.refresh()
- QMessageBox.information(self, self.tr("Import to Database"), self.tr("Import was successful."))
+ QMessageBox.information(
+ self, self.tr("Import to Database"), self.tr("Import was successful.")
+ )
return QDialog.accept(self)
def closeEvent(self, event):
diff --git a/python/plugins/db_manager/dlg_query_builder.py b/python/plugins/db_manager/dlg_query_builder.py
index 33998530d23b..fb893cf3ac21 100644
--- a/python/plugins/db_manager/dlg_query_builder.py
+++ b/python/plugins/db_manager/dlg_query_builder.py
@@ -26,14 +26,14 @@
from .db_plugins.plugin import VectorTable
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgQueryBuilder.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgQueryBuilder.ui"))
class FocusEventFilter(QObject):
def __init__(self, parent):
QObject.__init__(self, parent)
- self.focus = ''
+ self.focus = ""
def eventFilter(self, obj, event):
if event.type() == QEvent.Type.FocusIn:
@@ -63,7 +63,7 @@ def __init__(self, iface, db, parent=None, reset=False):
QDialog.__init__(self, parent)
self.iface = iface
self.db = db
- self.query = ''
+ self.query = ""
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.group.setMaximumHeight(self.ui.tab.sizeHint().height())
@@ -83,11 +83,11 @@ def __init__(self, iface, db, parent=None, reset=False):
self.coltables = []
self.ui.extract.setChecked(True)
# ComboBox default values
- self.ui.functions.insertItems(1, d['function'])
- self.ui.math.insertItems(1, d['math'])
- self.ui.aggregates.insertItems(1, d['aggregate'])
- self.ui.operators.insertItems(1, d['operator'])
- self.ui.stringfct.insertItems(1, d['string'])
+ self.ui.functions.insertItems(1, d["function"])
+ self.ui.math.insertItems(1, d["math"])
+ self.ui.aggregates.insertItems(1, d["aggregate"])
+ self.ui.operators.insertItems(1, d["operator"])
+ self.ui.stringfct.insertItems(1, d["string"])
# self.ui.Rtree.insertItems(1,rtreecommand)
# restore last query if needed
@@ -116,11 +116,19 @@ def __init__(self, iface, db, parent=None, reset=False):
self.ui.checkBox.stateChanged.connect(self.show_tables)
if self.db.explicitSpatialIndex():
- self.tablesGeo = [table for table in self.tables if isinstance(table, VectorTable)]
- tablesGeo = ['"%s"."%s"' % (table.name, table.geomColumn) for table in self.tablesGeo]
+ self.tablesGeo = [
+ table for table in self.tables if isinstance(table, VectorTable)
+ ]
+ tablesGeo = [
+ f'"{table.name}"."{table.geomColumn}"' for table in self.tablesGeo
+ ]
self.ui.table_target.insertItems(1, tablesGeo)
- self.idxTables = [table for table in self.tablesGeo if table.hasSpatialIndex()]
- idxTables = ['"%s"."%s"' % (table.name, table.geomColumn) for table in self.idxTables]
+ self.idxTables = [
+ table for table in self.tablesGeo if table.hasSpatialIndex()
+ ]
+ idxTables = [
+ f'"{table.name}"."{table.geomColumn}"' for table in self.idxTables
+ ]
self.ui.table_idx.insertItems(1, idxTables)
self.ui.usertree.clicked.connect(self.use_rtree)
@@ -199,16 +207,21 @@ def add_tables(self):
if len(tableObj) != 1:
return # No object with this name
self.table = tableObj[0]
- if (ag in self.coltables): # table already use
- response = QMessageBox.question(self, "Table already used", "Do you want to add table %s again?" % ag, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ if ag in self.coltables: # table already use
+ response = QMessageBox.question(
+ self,
+ "Table already used",
+ "Do you want to add table %s again?" % ag,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if response == QMessageBox.StandardButton.No:
return
ag = self.table.quotedName()
txt = self.ui.tab.text()
if (txt is None) or (txt in ("", " ")):
- self.ui.tab.setText('%s' % ag)
+ self.ui.tab.setText("%s" % ag)
else:
- self.ui.tab.setText('%s, %s' % (txt, ag))
+ self.ui.tab.setText(f"{txt}, {ag}")
self.ui.tables.setCurrentIndex(0)
def add_columns(self):
@@ -217,7 +230,12 @@ def add_columns(self):
ag = self.ui.columns.currentText()
if self.evt.focus == "where": # in where section
if ag in self.col_where: # column already called in where section
- response = QMessageBox.question(self, "Column already used in WHERE clause", "Do you want to add column %s again?" % ag, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ response = QMessageBox.question(
+ self,
+ "Column already used in WHERE clause",
+ "Do you want to add column %s again?" % ag,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if response == QMessageBox.StandardButton.No:
self.ui.columns.setCurrentIndex(0)
return
@@ -225,7 +243,12 @@ def add_columns(self):
self.col_where.append(ag)
elif self.evt.focus == "col":
if ag in self.col_col: # column already called in col section
- response = QMessageBox.question(self, "Column already used in COLUMNS section", "Do you want to add column %s again?" % ag, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ response = QMessageBox.question(
+ self,
+ "Column already used in COLUMNS section",
+ "Do you want to add column %s again?" % ag,
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if response == QMessageBox.StandardButton.No:
self.ui.columns.setCurrentIndex(0)
return
@@ -249,12 +272,12 @@ def add_columns(self):
def list_cols(self):
table = self.table
- if (table is None):
+ if table is None:
return
- if (table.name in self.coltables):
+ if table.name in self.coltables:
return
- columns = ['"%s"."%s"' % (table.name, col.name) for col in table.fields()]
+ columns = [f'"{table.name}"."{col.name}"' for col in table.fields()]
# add special '*' column:
columns = ['"%s".*' % table.name] + columns
self.coltables.append(table.name) # table columns have been listed
@@ -273,11 +296,13 @@ def list_values(self):
# recover column and table:
column = item.split(".") # "table".'column'
table = column[0]
- if column[1] == '*':
+ if column[1] == "*":
return
table = table[1:-1]
- qtable = [t for t in self.tables if t.name.lower() == table.lower()][0].quotedName()
+ qtable = [t for t in self.tables if t.name.lower() == table.lower()][
+ 0
+ ].quotedName()
if self.ui.extract.isChecked():
limit = 10
@@ -290,14 +315,14 @@ def query_item(self, index):
value = index.data(Qt.ItemDataRole.EditRole)
if value is None:
- queryWord = 'NULL'
+ queryWord = "NULL"
elif isinstance(value, (int, float)):
queryWord = str(value)
else:
queryWord = self.db.connector.quoteString(value)
- if queryWord.strip() != '':
- self.ui.where.insertPlainText(' ' + queryWord)
+ if queryWord.strip() != "":
+ self.ui.where.insertPlainText(" " + queryWord)
self.ui.where.setFocus()
def use_rtree(self):
@@ -308,12 +333,17 @@ def use_rtree(self):
tab_idx = idx.split(".")[0][1:-1] # remove "
col_idx = idx.split(".")[1][1:-1] # remove '
except:
- QMessageBox.warning(self, "Use R-Tree", "All fields are necessary", QMessageBox.StandardButton.Cancel)
+ QMessageBox.warning(
+ self,
+ "Use R-Tree",
+ "All fields are necessary",
+ QMessageBox.StandardButton.Cancel,
+ )
tgt = self.ui.table_target.currentText()
if tgt in (None, "", " ", "Table (Target)"):
return
- tgt_tab = tgt.split('.')[0][1:-1]
- tgt_col = tgt.split('.')[1][1:-1]
+ tgt_tab = tgt.split(".")[0][1:-1]
+ tgt_col = tgt.split(".")[1][1:-1]
sql = ""
if self.ui.where.toPlainText() not in (None, "", " "):
sql += "\nAND"
@@ -337,15 +367,15 @@ def validate(self):
query_group = str(self.ui.group.toPlainText())
query_order = str(self.ui.order.toPlainText())
query = ""
- if query_col.strip() != '':
- query += "SELECT %s \nFROM %s" % (query_col, query_table)
- if query_where.strip() != '':
+ if query_col.strip() != "":
+ query += f"SELECT {query_col} \nFROM {query_table}"
+ if query_where.strip() != "":
query += "\nWHERE %s" % query_where
- if query_group.strip() != '':
+ if query_group.strip() != "":
query += "\nGROUP BY %s" % query_group
- if query_order.strip() != '':
+ if query_order.strip() != "":
query += "\nORDER BY %s" % query_order
- if query == '':
+ if query == "":
return
self.query = query
@@ -377,11 +407,15 @@ def restoreLastQuery(self):
# list previous colist:
for tablename in self.coltables:
# Retrieve table object from table name:
- table = [table for table in self.tables if table.name.upper() == tablename.upper()]
+ table = [
+ table
+ for table in self.tables
+ if table.name.upper() == tablename.upper()
+ ]
if len(table) != 1:
break
table = table[0]
- columns = ['"%s"."%s"' % (table.name, col.name) for col in table.fields()]
+ columns = [f'"{table.name}"."{col.name}"' for col in table.fields()]
# first and second col combobox
end = self.ui.columns.count()
self.ui.columns.insertItems(end, columns)
diff --git a/python/plugins/db_manager/dlg_sql_layer_window.py b/python/plugins/db_manager/dlg_sql_layer_window.py
index 25bd49a1b204..6d3b6fb07b36 100644
--- a/python/plugins/db_manager/dlg_sql_layer_window.py
+++ b/python/plugins/db_manager/dlg_sql_layer_window.py
@@ -19,33 +19,31 @@
* *
***************************************************************************/
"""
+
from hashlib import md5
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, pyqtSignal
-from qgis.PyQt.QtWidgets import (QDialog,
- QWidget,
- QAction,
- QApplication,
- QStyledItemDelegate,
- QMessageBox
- )
-from qgis.PyQt.QtGui import (QKeySequence,
- QCursor,
- QClipboard,
- QIcon,
- QStandardItemModel,
- QStandardItem
- )
+from qgis.PyQt.QtWidgets import (
+ QDialog,
+ QWidget,
+ QAction,
+ QApplication,
+ QStyledItemDelegate,
+ QMessageBox,
+)
+from qgis.PyQt.QtGui import (
+ QKeySequence,
+ QCursor,
+ QClipboard,
+ QIcon,
+ QStandardItemModel,
+ QStandardItem,
+)
from qgis.PyQt.Qsci import QsciAPIs
from qgis.PyQt.QtXml import QDomDocument
-from qgis.core import (
- QgsProject,
- QgsDataSourceUri,
- QgsReadWriteContext,
- QgsMapLayerType
-)
+from qgis.core import QgsProject, QgsDataSourceUri, QgsReadWriteContext, QgsMapLayerType
from qgis.utils import OverrideCursor
from .db_plugins import createDbPlugin
@@ -63,7 +61,7 @@
gui.QgsCodeEditorSQL = SqlEdit
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgSqlLayerWindow.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgSqlLayerWindow.ui"))
import re
@@ -80,16 +78,16 @@ def __init__(self, iface, layer, parent=None):
uri = QgsDataSourceUri(layer.source())
dbplugin = None
db = None
- if layer.dataProvider().name() == 'postgres':
- dbplugin = createDbPlugin('postgis', 'postgres')
- elif layer.dataProvider().name() == 'spatialite':
- dbplugin = createDbPlugin('spatialite', 'spatialite')
- elif layer.dataProvider().name() == 'oracle':
- dbplugin = createDbPlugin('oracle', 'oracle')
- elif layer.dataProvider().name() == 'virtual':
- dbplugin = createDbPlugin('vlayers', 'virtual')
- elif layer.dataProvider().name() == 'ogr':
- dbplugin = createDbPlugin('gpkg', 'gpkg')
+ if layer.dataProvider().name() == "postgres":
+ dbplugin = createDbPlugin("postgis", "postgres")
+ elif layer.dataProvider().name() == "spatialite":
+ dbplugin = createDbPlugin("spatialite", "spatialite")
+ elif layer.dataProvider().name() == "oracle":
+ dbplugin = createDbPlugin("oracle", "oracle")
+ elif layer.dataProvider().name() == "virtual":
+ dbplugin = createDbPlugin("vlayers", "virtual")
+ elif layer.dataProvider().name() == "ogr":
+ dbplugin = createDbPlugin("gpkg", "gpkg")
if dbplugin:
dbplugin.connectToUri(uri)
db = dbplugin.db
@@ -97,13 +95,22 @@ def __init__(self, iface, layer, parent=None):
self.dbplugin = dbplugin
self.db = db
self.filter = ""
- self.allowMultiColumnPk = isinstance(db, PGDatabase) # at the moment only PostgreSQL allows a primary key to span multiple columns, SpatiaLite doesn't
- self.aliasSubQuery = isinstance(db, PGDatabase) # only PostgreSQL requires subqueries to be aliases
+ self.allowMultiColumnPk = isinstance(
+ db, PGDatabase
+ ) # at the moment only PostgreSQL allows a primary key to span multiple columns, SpatiaLite doesn't
+ self.aliasSubQuery = isinstance(
+ db, PGDatabase
+ ) # only PostgreSQL requires subqueries to be aliases
self.setupUi(self)
self.setWindowTitle(
- "%s - %s [%s]" % (self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString()))
+ "{} - {} [{}]".format(
+ self.windowTitle(),
+ db.connection().connectionName(),
+ db.connection().typeNameString(),
+ )
+ )
- self.defaultLayerName = self.tr('QueryLayer')
+ self.defaultLayerName = self.tr("QueryLayer")
if self.allowMultiColumnPk:
self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values"))
@@ -147,9 +154,12 @@ def __init__(self, iface, layer, parent=None):
self.uniqueCombo.setModel(self.uniqueModel)
if self.allowMultiColumnPk:
self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
- self.uniqueModel.itemChanged.connect(self.uniqueChanged) # react to the (un)checking of an item
+ self.uniqueModel.itemChanged.connect(
+ self.uniqueChanged
+ ) # react to the (un)checking of an item
self.uniqueCombo.lineEdit().textChanged.connect(
- self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly
+ self.uniqueTextChanged
+ ) # there are other events that change the displayed text and some of them can not be caught directly
self.layerTypeWidget.hide() # show if load as raster is supported
# self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
@@ -164,28 +174,36 @@ def __init__(self, iface, layer, parent=None):
# Update from layer
# First the SQL from QgsDataSourceUri table
- sql = uri.table().replace('\n', ' ').strip()
- if uri.keyColumn() == '_uid_':
- match = re.search(r'^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S | re.X | re.IGNORECASE)
+ sql = uri.table().replace("\n", " ").strip()
+ if uri.keyColumn() == "_uid_":
+ match = re.search(
+ r"^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$",
+ sql,
+ re.S | re.X | re.IGNORECASE,
+ )
if match:
sql = match.group(1)
else:
- match = re.search(r'^\((SELECT .+ FROM .+)\)$', sql, re.S | re.X | re.IGNORECASE)
+ match = re.search(
+ r"^\((SELECT .+ FROM .+)\)$", sql, re.S | re.X | re.IGNORECASE
+ )
if match:
sql = match.group(1)
# Need to check on table() since the parentheses were removed by the regexp
- if not uri.table().startswith('(') and not uri.table().endswith(')'):
+ if not uri.table().startswith("(") and not uri.table().endswith(")"):
schema = uri.schema()
- if schema and schema.upper() != 'PUBLIC':
- sql = 'SELECT * FROM {}.{}'.format(self.db.connector.quoteId(schema), self.db.connector.quoteId(sql))
+ if schema and schema.upper() != "PUBLIC":
+ sql = f"SELECT * FROM {self.db.connector.quoteId(schema)}.{self.db.connector.quoteId(sql)}"
else:
- sql = 'SELECT * FROM {}'.format(self.db.connector.quoteId(sql))
+ sql = f"SELECT * FROM {self.db.connector.quoteId(sql)}"
self.editSql.setText(sql)
self.executeSql()
# Then the columns
- self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchFlag.MatchExactly))
- if uri.keyColumn() != '_uid_':
+ self.geomCombo.setCurrentIndex(
+ self.geomCombo.findText(uri.geometryColumn(), Qt.MatchFlag.MatchExactly)
+ )
+ if uri.keyColumn() != "_uid_":
self.uniqueColumnCheck.setCheckState(Qt.CheckState.Checked)
if self.allowMultiColumnPk:
# Unchecked default values
@@ -193,7 +211,7 @@ def __init__(self, iface, layer, parent=None):
if item.checkState() == Qt.CheckState.Checked:
item.setCheckState(Qt.CheckState.Unchecked)
# Get key columns
- itemsData = uri.keyColumn().split(',')
+ itemsData = uri.keyColumn().split(",")
# Checked key columns
for keyColumn in itemsData:
for item in self.uniqueModel.findItems(keyColumn):
@@ -201,7 +219,9 @@ def __init__(self, iface, layer, parent=None):
else:
keyColumn = uri.keyColumn()
if self.uniqueModel.findItems(keyColumn):
- self.uniqueCombo.setCurrentIndex(self.uniqueCombo.findText(keyColumn, Qt.MatchFlag.MatchExactly))
+ self.uniqueCombo.setCurrentIndex(
+ self.uniqueCombo.findText(keyColumn, Qt.MatchFlag.MatchExactly)
+ )
# Finally layer name, filter and selectAtId
self.layerNameEdit.setText(layer.name())
@@ -210,21 +230,25 @@ def __init__(self, iface, layer, parent=None):
self.avoidSelectById.setCheckState(Qt.CheckState.Checked)
def getQueryHash(self, name):
- return 'q%s' % md5(name.encode('utf8')).hexdigest()
+ return "q%s" % md5(name.encode("utf8")).hexdigest()
def updatePresetButtonsState(self, *args):
"""Slot called when the combo box or the sql or the query name have changed:
- sets store button state"""
- self.presetStore.setEnabled(bool(self._getSqlQuery() and self.presetName.text()))
+ sets store button state"""
+ self.presetStore.setEnabled(
+ bool(self._getSqlQuery() and self.presetName.text())
+ )
self.presetDelete.setEnabled(bool(self.presetCombo.currentIndex() != -1))
def updatePresetsCombobox(self):
self.presetCombo.clear()
names = []
- entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
+ entries = QgsProject.instance().subkeyList("DBManager", "savedQueries")
for entry in entries:
- name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
+ name = QgsProject.instance().readEntry(
+ "DBManager", "savedQueries/" + entry + "/name"
+ )[0]
names.append(name)
for name in sorted(names):
@@ -236,8 +260,12 @@ def storePreset(self):
if query == "":
return
name = self.presetName.text()
- QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name', name)
- QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query', query)
+ QgsProject.instance().writeEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/name", name
+ )
+ QgsProject.instance().writeEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/query", query
+ )
index = self.presetCombo.findText(name)
if index == -1:
self.presetCombo.addItem(name)
@@ -247,12 +275,16 @@ def storePreset(self):
def deletePreset(self):
name = self.presetCombo.currentText()
- QgsProject.instance().removeEntry('DBManager', 'savedQueries/q' + self.getQueryHash(name))
+ QgsProject.instance().removeEntry(
+ "DBManager", "savedQueries/q" + self.getQueryHash(name)
+ )
self.presetCombo.removeItem(self.presetCombo.findText(name))
self.presetCombo.setCurrentIndex(-1)
def loadPreset(self, name):
- query = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[0]
+ query = QgsProject.instance().readEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/query"
+ )[0]
self.editSql.setText(query)
def clearSql(self):
@@ -280,7 +312,11 @@ def executeSql(self):
# set the new model
model = self.db.sqlResultModel(sql, self)
self.viewResult.setModel(model)
- self.lblResult.setText(self.tr("{0} rows, {1:.3f} seconds").format(model.affectedRows(), model.secs()))
+ self.lblResult.setText(
+ self.tr("{0} rows, {1:.3f} seconds").format(
+ model.affectedRows(), model.secs()
+ )
+ )
cols = self.viewResult.model().columnNames()
for col in cols:
quotedCols.append(self.db.connector.quoteId(col))
@@ -310,7 +346,9 @@ def _getSqlLayer(self, _filter):
and not self.allowMultiColumnPk
and self.uniqueCombo.currentIndex() >= 0
):
- uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
+ uniqueFieldName = self.uniqueModel.item(
+ self.uniqueCombo.currentIndex()
+ ).data()
else:
uniqueFieldName = None
hasGeomCol = self.hasGeometryCol.checkState() == Qt.CheckState.Checked
@@ -324,10 +362,14 @@ def _getSqlLayer(self, _filter):
return None
# remove a trailing ';' from query if present
- if query.strip().endswith(';'):
+ if query.strip().endswith(";"):
query = query.strip()[:-1]
- layerType = QgsMapLayerType.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayerType.RasterLayer
+ layerType = (
+ QgsMapLayerType.VectorLayer
+ if self.vectorRadio.isChecked()
+ else QgsMapLayerType.RasterLayer
+ )
# get a new layer name
names = []
@@ -344,8 +386,15 @@ def _getSqlLayer(self, _filter):
newLayerName = "%s_%d" % (layerName, index)
# create the layer
- layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
- self.avoidSelectById.isChecked(), _filter)
+ layer = self.db.toSqlLayer(
+ query,
+ geomFieldName,
+ uniqueFieldName,
+ newLayerName,
+ layerType,
+ self.avoidSelectById.isChecked(),
+ _filter,
+ )
if layer.isValid():
return layer
else:
@@ -371,7 +420,9 @@ def updateSqlLayer(self):
XMLMapLayers = XMLDocument.createElement("maplayers")
XMLMapLayer = XMLDocument.createElement("maplayer")
self.layer.writeLayerXml(XMLMapLayer, XMLDocument, QgsReadWriteContext())
- XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(layer.source())
+ XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(
+ layer.source()
+ )
XMLMapLayers.appendChild(XMLMapLayer)
XMLDocument.appendChild(XMLMapLayers)
self.layer.readLayerXml(XMLMapLayer, QgsReadWriteContext())
@@ -386,7 +437,7 @@ def fillColumnCombos(self):
with OverrideCursor(Qt.CursorShape.WaitCursor):
# remove a trailing ';' from query if present
- if query.strip().endswith(';'):
+ if query.strip().endswith(";"):
query = query.strip()[:-1]
# get all the columns
@@ -397,12 +448,14 @@ def fillColumnCombos(self):
aliasIndex = 0
while True:
alias = "_subQuery__%d" % aliasIndex
- escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
+ escaped = re.compile('\\b("?)' + re.escape(alias) + "\\1\\b")
if not escaped.search(query):
break
aliasIndex += 1
- sql = "SELECT * FROM (%s\n) AS %s LIMIT 0" % (str(query), connector.quoteId(alias))
+ sql = "SELECT * FROM ({}\n) AS {} LIMIT 0".format(
+ str(query), connector.quoteId(alias)
+ )
else:
sql = "SELECT * FROM (%s\n) WHERE 1=0" % str(query)
@@ -429,18 +482,20 @@ def fillColumnCombos(self):
def setColumnCombos(self, cols, quotedCols):
# get sensible default columns. do this before sorting in case there's hints in the column order (e.g., id is more likely to be first)
try:
- defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
+ defaultGeomCol = next(
+ col for col in cols if col in ["geom", "geometry", "the_geom", "way"]
+ )
except:
defaultGeomCol = None
try:
- defaultUniqueCol = [col for col in cols if 'id' in col][0]
+ defaultUniqueCol = [col for col in cols if "id" in col][0]
except:
defaultUniqueCol = None
colNames = sorted(zip(cols, quotedCols))
newItems = []
uniqueIsFilled = False
- for (col, quotedCol) in colNames:
+ for col, quotedCol in colNames:
item = QStandardItem(col)
item.setData(quotedCol)
item.setEnabled(True)
@@ -450,7 +505,10 @@ def setColumnCombos(self, cols, quotedCols):
matchingItems = self.uniqueModel.findItems(col)
if matchingItems:
item.setCheckState(matchingItems[0].checkState())
- uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.CheckState.Checked
+ uniqueIsFilled = (
+ uniqueIsFilled
+ or matchingItems[0].checkState() == Qt.CheckState.Checked
+ )
else:
item.setCheckState(Qt.CheckState.Unchecked)
newItems.append(item)
@@ -469,7 +527,9 @@ def setColumnCombos(self, cols, quotedCols):
oldGeometryColumn = self.geomCombo.currentText()
self.geomCombo.clear()
self.geomCombo.addItems(cols)
- self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchFlag.MatchExactly))
+ self.geomCombo.setCurrentIndex(
+ self.geomCombo.findText(oldGeometryColumn, Qt.MatchFlag.MatchExactly)
+ )
# set sensible default columns if the columns are not already set
try:
@@ -550,6 +610,7 @@ def uniqueTextChanged(self, text):
def setFilter(self):
from qgis.gui import QgsQueryBuilder
+
layer = self._getSqlLayer("")
if not layer:
return
@@ -566,9 +627,14 @@ def setHasChanged(self, hasChanged):
def close(self):
if self.hasChanged:
ret = QMessageBox.question(
- self, self.tr('Unsaved Changes?'),
- self.tr('There are unsaved changes. Do you want to keep them?'),
- QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Discard, QMessageBox.StandardButton.Cancel)
+ self,
+ self.tr("Unsaved Changes?"),
+ self.tr("There are unsaved changes. Do you want to keep them?"),
+ QMessageBox.StandardButton.Save
+ | QMessageBox.StandardButton.Cancel
+ | QMessageBox.StandardButton.Discard,
+ QMessageBox.StandardButton.Cancel,
+ )
if ret == QMessageBox.StandardButton.Save:
self.saveAsFilePreset()
diff --git a/python/plugins/db_manager/dlg_sql_window.py b/python/plugins/db_manager/dlg_sql_window.py
index 8109fa731ce3..fbf0f4c9fcb1 100644
--- a/python/plugins/db_manager/dlg_sql_window.py
+++ b/python/plugins/db_manager/dlg_sql_window.py
@@ -19,38 +19,35 @@
* *
***************************************************************************/
"""
+
from hashlib import md5
import os
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, pyqtSignal, QDir, QCoreApplication
-from qgis.PyQt.QtWidgets import (QDialog,
- QWidget,
- QAction,
- QApplication,
- QInputDialog,
- QStyledItemDelegate,
- QTableWidgetItem,
- QFileDialog,
- QMessageBox
- )
-from qgis.PyQt.QtGui import (QKeySequence,
- QCursor,
- QClipboard,
- QIcon,
- QStandardItemModel,
- QStandardItem
- )
+from qgis.PyQt.QtWidgets import (
+ QDialog,
+ QWidget,
+ QAction,
+ QApplication,
+ QInputDialog,
+ QStyledItemDelegate,
+ QTableWidgetItem,
+ QFileDialog,
+ QMessageBox,
+)
+from qgis.PyQt.QtGui import (
+ QKeySequence,
+ QCursor,
+ QClipboard,
+ QIcon,
+ QStandardItemModel,
+ QStandardItem,
+)
from qgis.PyQt.Qsci import QsciAPIs, QsciScintilla
-from qgis.core import (
- QgsProject,
- QgsApplication,
- QgsTask,
- QgsSettings,
- QgsMapLayerType
-)
+from qgis.core import QgsProject, QgsApplication, QgsTask, QgsSettings, QgsMapLayerType
from qgis.utils import OverrideCursor
from .db_plugins.plugin import BaseError
@@ -67,7 +64,7 @@
gui.QgsCodeEditorSQL = SqlEdit
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgSqlWindow.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgSqlWindow.ui"))
import re
@@ -76,13 +73,10 @@
def check_comments_in_sql(raw_sql_input):
lines = []
for line in raw_sql_input.splitlines():
- if not line.strip().startswith('--'):
- if '--' in line:
- comments = re.finditer(r'--', line)
- comment_positions = [
- match.start()
- for match in comments
- ]
+ if not line.strip().startswith("--"):
+ if "--" in line:
+ comments = re.finditer(r"--", line)
+ comment_positions = [match.start() for match in comments]
identifiers = re.finditer(r'"(?:[^"]|"")*"', line)
quotes = re.finditer(r"'(?:[^']|'')*'", line)
quote_positions = []
@@ -96,12 +90,12 @@ def check_comments_in_sql(raw_sql_input):
if comment >= quote_position[0] and comment < quote_position[1]:
unquoted_comments.remove(comment)
if len(unquoted_comments) > 0:
- lines.append(line[:unquoted_comments[0]])
+ lines.append(line[: unquoted_comments[0]])
else:
lines.append(line)
else:
lines.append(line)
- sql = ' '.join(lines)
+ sql = " ".join(lines)
return sql.strip()
@@ -119,14 +113,20 @@ def __init__(self, iface, db, parent=None):
self.connectionName = db.connection().connectionName()
self.filter = ""
self.modelAsync = None
- self.allowMultiColumnPk = isinstance(db,
- PGDatabase) # at the moment only PostgreSQL allows a primary key to span multiple columns, SpatiaLite doesn't
- self.aliasSubQuery = isinstance(db, PGDatabase) # only PostgreSQL requires subqueries to be aliases
+ self.allowMultiColumnPk = isinstance(
+ db, PGDatabase
+ ) # at the moment only PostgreSQL allows a primary key to span multiple columns, SpatiaLite doesn't
+ self.aliasSubQuery = isinstance(
+ db, PGDatabase
+ ) # only PostgreSQL requires subqueries to be aliases
self.setupUi(self)
self.setWindowTitle(
- self.tr("{0} - {1} [{2}]").format(self.windowTitle(), self.connectionName, self.dbType))
+ self.tr("{0} - {1} [{2}]").format(
+ self.windowTitle(), self.connectionName, self.dbType
+ )
+ )
- self.defaultLayerName = self.tr('QueryLayer')
+ self.defaultLayerName = self.tr("QueryLayer")
if self.allowMultiColumnPk:
self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values"))
@@ -140,7 +140,9 @@ def __init__(self, iface, db, parent=None):
self.editSql.textChanged.connect(lambda: self.setHasChanged(True))
settings = QgsSettings()
- self.history = settings.value('DB_Manager/queryHistory/' + self.dbType, {self.connectionName: []})
+ self.history = settings.value(
+ "DB_Manager/queryHistory/" + self.dbType, {self.connectionName: []}
+ )
if self.connectionName not in self.history:
self.history[self.connectionName] = []
@@ -188,9 +190,12 @@ def __init__(self, iface, db, parent=None):
self.uniqueCombo.setModel(self.uniqueModel)
if self.allowMultiColumnPk:
self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
- self.uniqueModel.itemChanged.connect(self.uniqueChanged) # react to the (un)checking of an item
+ self.uniqueModel.itemChanged.connect(
+ self.uniqueChanged
+ ) # react to the (un)checking of an item
self.uniqueCombo.lineEdit().textChanged.connect(
- self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly
+ self.uniqueTextChanged
+ ) # there are other events that change the displayed text and some of them can not be caught directly
# hide the load query as layer if feature is not supported
self._loadAsLayerAvailable = self.db.connector.hasCustomQuerySupport()
@@ -230,9 +235,9 @@ def populateQueryHistory(self):
for i in range(len(dictlist)):
self.queryHistoryTableWidget.insertRow(0)
- queryItem = QTableWidgetItem(dictlist[i]['query'])
- rowsItem = QTableWidgetItem(str(dictlist[i]['rows']))
- durationItem = QTableWidgetItem(str(dictlist[i]['secs']))
+ queryItem = QTableWidgetItem(dictlist[i]["query"])
+ rowsItem = QTableWidgetItem(str(dictlist[i]["rows"]))
+ durationItem = QTableWidgetItem(str(dictlist[i]["secs"]))
self.queryHistoryTableWidget.setItem(0, 0, queryItem)
self.queryHistoryTableWidget.setItem(0, 1, rowsItem)
self.queryHistoryTableWidget.setItem(0, 2, durationItem)
@@ -245,23 +250,25 @@ def writeQueryHistory(self, sql, affectedRows, secs):
self.history[self.connectionName].pop(0)
settings = QgsSettings()
- self.history[self.connectionName].append({'query': sql,
- 'rows': affectedRows,
- 'secs': secs})
- settings.setValue('DB_Manager/queryHistory/' + self.dbType, self.history)
+ self.history[self.connectionName].append(
+ {"query": sql, "rows": affectedRows, "secs": secs}
+ )
+ settings.setValue("DB_Manager/queryHistory/" + self.dbType, self.history)
self.populateQueryHistory()
def getQueryHash(self, name):
- return 'q%s' % md5(name.encode('utf8')).hexdigest()
+ return "q%s" % md5(name.encode("utf8")).hexdigest()
def updatePresetsCombobox(self):
self.presetCombo.clear()
names = []
- entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
+ entries = QgsProject.instance().subkeyList("DBManager", "savedQueries")
for entry in entries:
- name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
+ name = QgsProject.instance().readEntry(
+ "DBManager", "savedQueries/" + entry + "/name"
+ )[0]
names.append(name)
for name in sorted(names):
@@ -273,8 +280,12 @@ def storePreset(self):
if query == "":
return
name = str(self.presetName.text())
- QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name', name)
- QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query', query)
+ QgsProject.instance().writeEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/name", name
+ )
+ QgsProject.instance().writeEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/query", query
+ )
index = self.presetCombo.findText(name)
if index == -1:
self.presetCombo.addItem(name)
@@ -284,36 +295,35 @@ def storePreset(self):
def saveAsFilePreset(self):
settings = QgsSettings()
- lastDir = settings.value('DB_Manager/lastDirSQLFIle', "")
+ lastDir = settings.value("DB_Manager/lastDirSQLFIle", "")
query = self.editSql.text()
if query == "":
return
filename, _ = QFileDialog.getSaveFileName(
- self,
- self.tr('Save SQL Query'),
- lastDir,
- self.tr("SQL File (*.sql *.SQL)"))
+ self, self.tr("Save SQL Query"), lastDir, self.tr("SQL File (*.sql *.SQL)")
+ )
if filename:
- if not filename.lower().endswith('.sql'):
+ if not filename.lower().endswith(".sql"):
filename += ".sql"
- with open(filename, 'w') as f:
+ with open(filename, "w") as f:
f.write(query)
lastDir = os.path.dirname(filename)
- settings.setValue('DB_Manager/lastDirSQLFile', lastDir)
+ settings.setValue("DB_Manager/lastDirSQLFile", lastDir)
def loadFilePreset(self):
settings = QgsSettings()
- lastDir = settings.value('DB_Manager/lastDirSQLFIle', "")
+ lastDir = settings.value("DB_Manager/lastDirSQLFIle", "")
filename, _ = QFileDialog.getOpenFileName(
self,
self.tr("Load SQL Query"),
lastDir,
- self.tr("SQL File (*.sql *.SQL);;All Files (*)"))
+ self.tr("SQL File (*.sql *.SQL);;All Files (*)"),
+ )
if filename:
with open(filename) as f:
@@ -321,16 +331,20 @@ def loadFilePreset(self):
for line in f:
self.editSql.insertText(line)
lastDir = os.path.dirname(filename)
- settings.setValue('DB_Manager/lastDirSQLFile', lastDir)
+ settings.setValue("DB_Manager/lastDirSQLFile", lastDir)
def deletePreset(self):
name = self.presetCombo.currentText()
- QgsProject.instance().removeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name))
+ QgsProject.instance().removeEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name)
+ )
self.presetCombo.removeItem(self.presetCombo.findText(name))
self.presetCombo.setCurrentIndex(-1)
def loadPreset(self, name):
- query = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[0]
+ query = QgsProject.instance().readEntry(
+ "DBManager", "savedQueries/" + self.getQueryHash(name) + "/query"
+ )[0]
self.editSql.setText(query)
def loadAsLayerToggled(self, checked):
@@ -391,16 +405,19 @@ def executeSqlCompleted(self):
model = self.modelAsync.model
self.showError(None)
self.viewResult.setModel(model)
- self.lblResult.setText(self.tr("{0} rows, {1:.3f} seconds").format(model.affectedRows(), model.secs()))
+ self.lblResult.setText(
+ self.tr("{0} rows, {1:.3f} seconds").format(
+ model.affectedRows(), model.secs()
+ )
+ )
cols = self.viewResult.model().columnNames()
- quotedCols = [
- self.db.connector.quoteId(col)
- for col in cols
- ]
+ quotedCols = [self.db.connector.quoteId(col) for col in cols]
self.setColumnCombos(cols, quotedCols)
- self.writeQueryHistory(self.modelAsync.task.sql, model.affectedRows(), model.secs())
+ self.writeQueryHistory(
+ self.modelAsync.task.sql, model.affectedRows(), model.secs()
+ )
self.update()
elif not self.modelAsync.canceled:
self.showError(self.modelAsync.error)
@@ -433,7 +450,7 @@ def executeSql(self):
return
def showError(self, error):
- '''Shows the error or hides it if error is None'''
+ """Shows the error or hides it if error is None"""
if error:
self.viewResult.setVisible(False)
self.errorText.setVisible(True)
@@ -456,7 +473,9 @@ def _getSqlLayer(self, _filter):
and not self.allowMultiColumnPk
and self.uniqueCombo.currentIndex() >= 0
):
- uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
+ uniqueFieldName = self.uniqueModel.item(
+ self.uniqueCombo.currentIndex()
+ ).data()
else:
uniqueFieldName = None
hasGeomCol = self.hasGeometryCol.checkState() == Qt.CheckState.Checked
@@ -470,10 +489,14 @@ def _getSqlLayer(self, _filter):
return None
# remove a trailing ';' from query if present
- if query.strip().endswith(';'):
+ if query.strip().endswith(";"):
query = query.strip()[:-1]
- layerType = QgsMapLayerType.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayerType.RasterLayer
+ layerType = (
+ QgsMapLayerType.VectorLayer
+ if self.vectorRadio.isChecked()
+ else QgsMapLayerType.RasterLayer
+ )
# get a new layer name
names = []
@@ -490,12 +513,23 @@ def _getSqlLayer(self, _filter):
newLayerName = "%s_%d" % (layerName, index)
# create the layer
- layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
- self.avoidSelectById.isChecked(), _filter)
+ layer = self.db.toSqlLayer(
+ query,
+ geomFieldName,
+ uniqueFieldName,
+ newLayerName,
+ layerType,
+ self.avoidSelectById.isChecked(),
+ _filter,
+ )
if layer.isValid():
return layer
else:
- e = BaseError(self.tr("There was an error creating the SQL layer, please check the logs for further information."))
+ e = BaseError(
+ self.tr(
+ "There was an error creating the SQL layer, please check the logs for further information."
+ )
+ )
DlgDbError.showError(e, self)
return None
@@ -514,7 +548,7 @@ def fillColumnCombos(self):
with OverrideCursor(Qt.CursorShape.WaitCursor):
# remove a trailing ';' from query if present
- if query.strip().endswith(';'):
+ if query.strip().endswith(";"):
query = query.strip()[:-1]
# get all the columns
@@ -525,12 +559,14 @@ def fillColumnCombos(self):
aliasIndex = 0
while True:
alias = "_subQuery__%d" % aliasIndex
- escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
+ escaped = re.compile('\\b("?)' + re.escape(alias) + "\\1\\b")
if not escaped.search(query):
break
aliasIndex += 1
- sql = "SELECT * FROM (%s\n) AS %s LIMIT 0" % (str(query), connector.quoteId(alias))
+ sql = "SELECT * FROM ({}\n) AS {} LIMIT 0".format(
+ str(query), connector.quoteId(alias)
+ )
else:
sql = "SELECT * FROM (%s\n) WHERE 1=0" % str(query)
@@ -557,18 +593,20 @@ def fillColumnCombos(self):
def setColumnCombos(self, cols, quotedCols):
# get sensible default columns. do this before sorting in case there's hints in the column order (e.g., id is more likely to be first)
try:
- defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
+ defaultGeomCol = next(
+ col for col in cols if col in ["geom", "geometry", "the_geom", "way"]
+ )
except:
defaultGeomCol = None
try:
- defaultUniqueCol = [col for col in cols if 'id' in col][0]
+ defaultUniqueCol = [col for col in cols if "id" in col][0]
except:
defaultUniqueCol = None
colNames = sorted(zip(cols, quotedCols))
newItems = []
uniqueIsFilled = False
- for (col, quotedCol) in colNames:
+ for col, quotedCol in colNames:
item = QStandardItem(col)
item.setData(quotedCol)
item.setEnabled(True)
@@ -578,7 +616,10 @@ def setColumnCombos(self, cols, quotedCols):
matchingItems = self.uniqueModel.findItems(col)
if matchingItems:
item.setCheckState(matchingItems[0].checkState())
- uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.CheckState.Checked
+ uniqueIsFilled = (
+ uniqueIsFilled
+ or matchingItems[0].checkState() == Qt.CheckState.Checked
+ )
else:
item.setCheckState(Qt.CheckState.Unchecked)
newItems.append(item)
@@ -597,7 +638,9 @@ def setColumnCombos(self, cols, quotedCols):
oldGeometryColumn = self.geomCombo.currentText()
self.geomCombo.clear()
self.geomCombo.addItems(cols)
- self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchFlag.MatchExactly))
+ self.geomCombo.setCurrentIndex(
+ self.geomCombo.findText(oldGeometryColumn, Qt.MatchFlag.MatchExactly)
+ )
# set sensible default columns if the columns are not already set
try:
@@ -655,7 +698,9 @@ def displayQueryBuilder(self):
self.editSql.setText(dlg.query)
def createView(self):
- name, ok = QInputDialog.getText(None, self.tr("View Name"), self.tr("View name"))
+ name, ok = QInputDialog.getText(
+ None, self.tr("View Name"), self.tr("View name")
+ )
if ok:
try:
self.db.connector.createSpatialView(name, self._getExecutableSqlQuery())
@@ -672,7 +717,7 @@ def _getExecutableSqlQuery(self):
sql = self._getSqlQuery().strip()
uncommented_sql = check_comments_in_sql(sql)
- uncommented_sql = uncommented_sql.rstrip(';')
+ uncommented_sql = uncommented_sql.rstrip(";")
return uncommented_sql
def uniqueChanged(self):
@@ -691,6 +736,7 @@ def uniqueTextChanged(self, text):
def setFilter(self):
from qgis.gui import QgsQueryBuilder
+
layer = self._getSqlLayer("")
if not layer:
return
@@ -707,9 +753,14 @@ def setHasChanged(self, hasChanged):
def close(self):
if self.hasChanged:
ret = QMessageBox.question(
- self, self.tr('Unsaved Changes?'),
- self.tr('There are unsaved changes. Do you want to keep them?'),
- QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Discard, QMessageBox.StandardButton.Cancel)
+ self,
+ self.tr("Unsaved Changes?"),
+ self.tr("There are unsaved changes. Do you want to keep them?"),
+ QMessageBox.StandardButton.Save
+ | QMessageBox.StandardButton.Cancel
+ | QMessageBox.StandardButton.Discard,
+ QMessageBox.StandardButton.Cancel,
+ )
if ret == QMessageBox.StandardButton.Save:
self.saveAsFilePreset()
diff --git a/python/plugins/db_manager/dlg_table_properties.py b/python/plugins/db_manager/dlg_table_properties.py
index 36b9d7357162..879d712ac99e 100644
--- a/python/plugins/db_manager/dlg_table_properties.py
+++ b/python/plugins/db_manager/dlg_table_properties.py
@@ -26,7 +26,11 @@
from qgis.utils import OverrideCursor
-from .db_plugins.data_model import TableFieldsModel, TableConstraintsModel, TableIndexesModel
+from .db_plugins.data_model import (
+ TableFieldsModel,
+ TableConstraintsModel,
+ TableIndexesModel,
+)
from .db_plugins.plugin import BaseError, DbError
from .dlg_db_error import DlgDbError
@@ -37,7 +41,7 @@
from .gui_utils import GuiUtils
-Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgTableProperties.ui'))
+Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path("DlgTableProperties.ui"))
class DlgTableProperties(QDialog, Ui_Dialog):
@@ -95,8 +99,16 @@ def checkSupports(self):
self.btnEditColumn.setEnabled(allowEditColumns)
self.btnDeleteColumn.setEnabled(allowEditColumns)
- self.btnAddGeometryColumn.setEnabled(self.db.connector.canAddGeometryColumn((self.table.schemaName(), self.table.name)))
- self.btnAddSpatialIndex.setEnabled(self.db.connector.canAddSpatialIndex((self.table.schemaName(), self.table.name)))
+ self.btnAddGeometryColumn.setEnabled(
+ self.db.connector.canAddGeometryColumn(
+ (self.table.schemaName(), self.table.name)
+ )
+ )
+ self.btnAddSpatialIndex.setEnabled(
+ self.db.connector.canAddSpatialIndex(
+ (self.table.schemaName(), self.table.name)
+ )
+ )
def populateViews(self):
self.populateFields()
@@ -104,7 +116,7 @@ def populateViews(self):
self.populateIndexes()
def populateFields(self):
- """ load field information from database """
+ """load field information from database"""
m = self.viewFields.model()
m.clear()
@@ -115,16 +127,18 @@ def populateFields(self):
self.viewFields.resizeColumnToContents(col)
def currentColumn(self):
- """ returns row index of selected column """
+ """returns row index of selected column"""
sel = self.viewFields.selectionModel()
indexes = sel.selectedRows()
if len(indexes) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No columns were selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No columns were selected.")
+ )
return -1
return indexes[0].row()
def addColumn(self):
- """ open dialog to set column info and add column to table """
+ """open dialog to set column info and add column to table"""
dlg = DlgFieldProperties(self, None, self.table)
if not dlg.exec():
return
@@ -140,14 +154,14 @@ def addColumn(self):
DlgDbError.showError(e, self)
def addGeometryColumn(self):
- """ open dialog to add geometry column """
+ """open dialog to add geometry column"""
dlg = DlgAddGeometryColumn(self, self.table)
if not dlg.exec():
return
self.refresh()
def editColumn(self):
- """ open dialog to change column info and alter table appropriately """
+ """open dialog to change column info and alter table appropriately"""
index = self.currentColumn()
if index == -1:
return
@@ -165,13 +179,19 @@ def editColumn(self):
with OverrideCursor(Qt.CursorShape.WaitCursor):
self.aboutToChangeTable.emit()
try:
- fld.update(new_fld.name, new_fld.type2String(), new_fld.notNull, new_fld.default2String(), new_fld.comment)
+ fld.update(
+ new_fld.name,
+ new_fld.type2String(),
+ new_fld.notNull,
+ new_fld.default2String(),
+ new_fld.comment,
+ )
self.refresh()
except BaseError as e:
DlgDbError.showError(e, self)
def deleteColumn(self):
- """Deletes currently selected column """
+ """Deletes currently selected column"""
index = self.currentColumn()
if index == -1:
return
@@ -179,9 +199,12 @@ def deleteColumn(self):
m = self.viewFields.model()
fld = m.getObject(index)
- res = QMessageBox.question(self, self.tr("Delete Column"),
- self.tr("Are you sure you want to delete column '{0}'?").format(fld.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ self,
+ self.tr("Delete Column"),
+ self.tr("Are you sure you want to delete column '{0}'?").format(fld.name),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
@@ -214,7 +237,7 @@ def hideConstraints(self):
self.tabs.setTabEnabled(index, False)
def addConstraint(self):
- """Adds primary key or unique constraint """
+ """Adds primary key or unique constraint"""
dlg = DlgCreateConstraint(self, self.table)
if not dlg.exec():
@@ -222,7 +245,7 @@ def addConstraint(self):
self.refresh()
def deleteConstraint(self):
- """Deletes a constraint """
+ """Deletes a constraint"""
index = self.currentConstraint()
if index == -1:
@@ -231,9 +254,14 @@ def deleteConstraint(self):
m = self.viewConstraints.model()
constr = m.getObject(index)
- res = QMessageBox.question(self, self.tr("Delete Constraint"),
- self.tr("Are you sure you want to delete constraint '{0}'?").format(constr.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ self,
+ self.tr("Delete Constraint"),
+ self.tr("Are you sure you want to delete constraint '{0}'?").format(
+ constr.name
+ ),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
@@ -246,11 +274,13 @@ def deleteConstraint(self):
DlgDbError.showError(e, self)
def currentConstraint(self):
- """ returns row index of selected index """
+ """returns row index of selected index"""
sel = self.viewConstraints.selectionModel()
indexes = sel.selectedRows()
if len(indexes) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No constraints were selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No constraints were selected.")
+ )
return -1
return indexes[0].row()
@@ -275,21 +305,30 @@ def hideIndexes(self):
self.tabs.setTabEnabled(index, False)
def createIndex(self):
- """Creates an index """
+ """Creates an index"""
dlg = DlgCreateIndex(self, self.table)
if not dlg.exec():
return
self.refresh()
def createSpatialIndex(self):
- """Creates spatial index for the geometry column """
+ """Creates spatial index for the geometry column"""
if self.table.type != self.table.VectorType:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("The selected table has no geometry."))
+ QMessageBox.information(
+ self,
+ self.tr("DB Manager"),
+ self.tr("The selected table has no geometry."),
+ )
return
- res = QMessageBox.question(self, self.tr("Create Spatial Index"),
- self.tr("Create spatial index for field {0}?").format(self.table.geomColumn),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ self,
+ self.tr("Create Spatial Index"),
+ self.tr("Create spatial index for field {0}?").format(
+ self.table.geomColumn
+ ),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
@@ -304,16 +343,18 @@ def createSpatialIndex(self):
DlgDbError.showError(e, self)
def currentIndex(self):
- """ returns row index of selected index """
+ """returns row index of selected index"""
sel = self.viewIndexes.selectionModel()
indexes = sel.selectedRows()
if len(indexes) == 0:
- QMessageBox.information(self, self.tr("DB Manager"), self.tr("No indices were selected."))
+ QMessageBox.information(
+ self, self.tr("DB Manager"), self.tr("No indices were selected.")
+ )
return -1
return indexes[0].row()
def deleteIndex(self):
- """Deletes currently selected index """
+ """Deletes currently selected index"""
index = self.currentIndex()
if index == -1:
return
@@ -321,9 +362,12 @@ def deleteIndex(self):
m = self.viewIndexes.model()
idx = m.getObject(index)
- res = QMessageBox.question(self, self.tr("Delete Index"),
- self.tr("Are you sure you want to delete index '{0}'?").format(idx.name),
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
+ res = QMessageBox.question(
+ self,
+ self.tr("Delete Index"),
+ self.tr("Are you sure you want to delete index '{0}'?").format(idx.name),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
if res != QMessageBox.StandardButton.Yes:
return
@@ -347,7 +391,9 @@ def createComment(self):
return
self.refresh()
# Display successful message
- QMessageBox.information(self, self.tr("Add comment"), self.tr("Table successfully commented"))
+ QMessageBox.information(
+ self, self.tr("Add comment"), self.tr("Table successfully commented")
+ )
def deleteComment(self):
"""Drops the comment on the selected table"""
@@ -360,6 +406,8 @@ def deleteComment(self):
return
self.refresh()
# Refresh line edit, put a void comment
- self.viewComment.setText('')
+ self.viewComment.setText("")
# Display successful message
- QMessageBox.information(self, self.tr("Delete comment"), self.tr("Comment deleted"))
+ QMessageBox.information(
+ self, self.tr("Delete comment"), self.tr("Comment deleted")
+ )
diff --git a/python/plugins/db_manager/gui_utils.py b/python/plugins/db_manager/gui_utils.py
index 2d9994538568..b0b169f4a0a4 100644
--- a/python/plugins/db_manager/gui_utils.py
+++ b/python/plugins/db_manager/gui_utils.py
@@ -14,11 +14,7 @@
import os
from typing import Optional, Dict
-from qgis.PyQt.QtGui import (
- QIcon,
- QImage,
- QPixmap
-)
+from qgis.PyQt.QtGui import QIcon, QImage, QPixmap
class GuiUtils:
@@ -26,7 +22,7 @@ class GuiUtils:
Utilities for GUI plugin components
"""
- ICON_CACHE: Dict[str, QIcon] = {}
+ ICON_CACHE: dict[str, QIcon] = {}
@staticmethod
def get_icon(icon: str) -> QIcon:
@@ -62,12 +58,9 @@ def get_icon_svg(icon: str) -> str:
:param icon: icon name (base part of file name)
:return: icon svg path
"""
- path = os.path.join(
- os.path.dirname(__file__),
- 'icons',
- icon + '.svg')
+ path = os.path.join(os.path.dirname(__file__), "icons", icon + ".svg")
if not os.path.exists(path):
- return ''
+ return ""
return path
@@ -76,11 +69,8 @@ def get_pixmap_path(icon: str) -> Optional[str]:
"""
Returns the path to a pixmap icon
"""
- for suffix in ('.png', '.gif', '.xpm'):
- path = os.path.join(
- os.path.dirname(__file__),
- 'icons',
- icon + suffix)
+ for suffix in (".png", ".gif", ".xpm"):
+ path = os.path.join(os.path.dirname(__file__), "icons", icon + suffix)
if os.path.exists(path):
return path
@@ -107,11 +97,8 @@ def get_ui_file_path(file: str) -> str:
:param file: file name (uifile name)
:return: ui file path
"""
- path = os.path.join(
- os.path.dirname(__file__),
- 'ui',
- file)
+ path = os.path.join(os.path.dirname(__file__), "ui", file)
if not os.path.exists(path):
- return ''
+ return ""
return path
diff --git a/python/plugins/db_manager/info_viewer.py b/python/plugins/db_manager/info_viewer.py
index 67d0c6730974..567c959a9b2c 100644
--- a/python/plugins/db_manager/info_viewer.py
+++ b/python/plugins/db_manager/info_viewer.py
@@ -95,14 +95,21 @@ def _clear(self):
def _showPluginInfo(self):
from .db_plugins import getDbPluginErrors
- html = '
'
try:
if connection.database() is None:
@@ -110,38 +117,47 @@ def _showDatabaseInfo(self, connection):
else:
html += connection.database().info().toHtml()
except DbError as e:
- html += '
%s
' % str(e).replace('\n', ' ')
- html += '
'
+ html += '
%s
' % str(e).replace("\n", " ")
+ html += "
"
self.setHtml(html)
def _showSchemaInfo(self, schema):
- html = '
%s
' % schema.name
+ html = (
+ '
%s
'
+ % schema.name
+ )
html += '
'
try:
html += schema.info().toHtml()
except DbError as e:
- html += '
%s
' % str(e).replace('\n', ' ')
+ html += '
%s
' % str(e).replace("\n", " ")
html += "
"
self.setHtml(html)
def _showTableInfo(self, table):
- html = '
%s
' % table.name
+ html = (
+ '
%s
'
+ % table.name
+ )
html += '
'
try:
html += table.info().toHtml()
except DbError as e:
- html += '
%s
' % str(e).replace('\n', ' ')
- html += '
'
+ html += '
%s
' % str(e).replace("\n", " ")
+ html += ""
self.setHtml(html)
return True
def setHtml(self, html):
# convert special tags
- warning_icon_path = GuiUtils.get_pixmap_path('warning-20px')
- html = str(html).replace('', f' ')
+ warning_icon_path = GuiUtils.get_pixmap_path("warning-20px")
+ html = str(html).replace(
+ "", f' '
+ )
# add default style
- html = """
+ html = (
+ """
@@ -158,7 +174,9 @@ def setHtml(self, html):
%s
-""" % html
+"""
+ % html
+ )
# print(">>>>>\n", html, "\n<<<<<<")
return QTextBrowser.setHtml(self, html)
diff --git a/python/plugins/db_manager/layer_preview.py b/python/plugins/db_manager/layer_preview.py
index 04ddcff975d9..02afb303331b 100644
--- a/python/plugins/db_manager/layer_preview.py
+++ b/python/plugins/db_manager/layer_preview.py
@@ -42,7 +42,9 @@ def __init__(self, parent=None):
# reuse settings from QGIS
settings = QgsSettings()
- self.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type=bool))
+ self.enableAntiAliasing(
+ settings.value("/qgis/enable_anti_aliasing", False, type=bool)
+ )
zoomFactor = settings.value("/qgis/zoom_factor", 2, type=float)
self.setWheelFactor(zoomFactor)
@@ -59,7 +61,10 @@ def loadPreview(self, item):
self._clear()
- if isinstance(item, Table) and item.type in [Table.VectorType, Table.RasterType]:
+ if isinstance(item, Table) and item.type in [
+ Table.VectorType,
+ Table.RasterType,
+ ]:
# update the preview, but first let the manager chance to show the canvas
def runPrev():
return self._loadTablePreview(item)
@@ -75,7 +80,7 @@ def setDirty(self, val=True):
self.dirty = val
def _clear(self):
- """ remove any layers from preview canvas """
+ """remove any layers from preview canvas"""
if self.item is not None:
# skip exception on RuntimeError fixes #6892
try:
@@ -88,7 +93,7 @@ def _clear(self):
self._loadTablePreview(None)
def _loadTablePreview(self, table, limit=False):
- """ if has geometry column load to map canvas """
+ """if has geometry column load to map canvas"""
with OverrideCursor(Qt.CursorShape.WaitCursor):
self.freeze()
vl = None
@@ -100,13 +105,22 @@ def _loadTablePreview(self, table, limit=False):
if uniqueField is None:
self.parent.tabs.setCurrentWidget(self.parent.info)
self.parent.infoBar.pushMessage(
- QApplication.translate("DBManagerPlugin", "Unable to find a valid unique field"),
- Qgis.MessageLevel.Warning, self.parent.iface.messageTimeout())
+ QApplication.translate(
+ "DBManagerPlugin", "Unable to find a valid unique field"
+ ),
+ Qgis.MessageLevel.Warning,
+ self.parent.iface.messageTimeout(),
+ )
return
uri = table.database().uri()
- uri.setDataSource("", "(SELECT * FROM %s LIMIT 1000)" % table.quotedName(), table.geomColumn, "",
- uniqueField.name)
+ uri.setDataSource(
+ "",
+ "(SELECT * FROM %s LIMIT 1000)" % table.quotedName(),
+ table.geomColumn,
+ "",
+ uniqueField.name,
+ )
provider = table.database().dbplugin().providerName()
vl = QgsVectorLayer(uri.uri(False), table.name, provider)
else:
diff --git a/python/plugins/db_manager/sql_dictionary.py b/python/plugins/db_manager/sql_dictionary.py
index e22449dffa21..3bdf96e93b9c 100644
--- a/python/plugins/db_manager/sql_dictionary.py
+++ b/python/plugins/db_manager/sql_dictionary.py
@@ -15,36 +15,149 @@
***************************************************************************
"""
-__author__ = 'Giuseppe Sucameli'
-__date__ = 'April 2012'
-__copyright__ = '(C) 2012, Giuseppe Sucameli'
+__author__ = "Giuseppe Sucameli"
+__date__ = "April 2012"
+__copyright__ = "(C) 2012, Giuseppe Sucameli"
# GENERIC SQL DICTIONARY
# keywords
keywords = [
- "action", "add", "after", "all", "alter", "analyze", "and", "as", "asc",
- "before", "begin", "between", "by", "cascade", "case", "cast", "check",
- "collate", "column", "commit", "constraint", "create", "cross", "current_date",
- "current_time", "current_timestamp", "default", "deferrable", "deferred",
- "delete", "desc", "distinct", "drop", "each", "else", "end", "escape",
- "except", "exists", "for", "foreign", "from", "full", "group", "having",
- "ignore", "immediate", "in", "initially", "inner", "insert", "intersect",
- "into", "is", "isnull", "join", "key", "left", "like", "limit", "match",
- "natural", "no", "not", "notnull", "null", "of", "offset", "on", "or", "order",
- "outer", "primary", "references", "release", "restrict", "right", "rollback",
- "row", "savepoint", "select", "set", "table", "temporary", "then", "to",
- "transaction", "trigger", "union", "unique", "update", "using", "values",
- "view", "when", "where"
+ "action",
+ "add",
+ "after",
+ "all",
+ "alter",
+ "analyze",
+ "and",
+ "as",
+ "asc",
+ "before",
+ "begin",
+ "between",
+ "by",
+ "cascade",
+ "case",
+ "cast",
+ "check",
+ "collate",
+ "column",
+ "commit",
+ "constraint",
+ "create",
+ "cross",
+ "current_date",
+ "current_time",
+ "current_timestamp",
+ "default",
+ "deferrable",
+ "deferred",
+ "delete",
+ "desc",
+ "distinct",
+ "drop",
+ "each",
+ "else",
+ "end",
+ "escape",
+ "except",
+ "exists",
+ "for",
+ "foreign",
+ "from",
+ "full",
+ "group",
+ "having",
+ "ignore",
+ "immediate",
+ "in",
+ "initially",
+ "inner",
+ "insert",
+ "intersect",
+ "into",
+ "is",
+ "isnull",
+ "join",
+ "key",
+ "left",
+ "like",
+ "limit",
+ "match",
+ "natural",
+ "no",
+ "not",
+ "notnull",
+ "null",
+ "of",
+ "offset",
+ "on",
+ "or",
+ "order",
+ "outer",
+ "primary",
+ "references",
+ "release",
+ "restrict",
+ "right",
+ "rollback",
+ "row",
+ "savepoint",
+ "select",
+ "set",
+ "table",
+ "temporary",
+ "then",
+ "to",
+ "transaction",
+ "trigger",
+ "union",
+ "unique",
+ "update",
+ "using",
+ "values",
+ "view",
+ "when",
+ "where",
]
# functions
functions = [
- "abs", "changes", "coalesce", "glob", "ifnull", "hex", "last_insert_rowid",
- "length", "like", "lower", "ltrim", "max", "min", "nullif", "quote", "random",
- "randomblob", "replace", "round", "rtrim", "soundex", "total_change", "trim",
- "typeof", "upper", "zeroblob", "date", "datetime", "julianday", "strftime",
- "avg", "count", "group_concat", "sum", "total"
+ "abs",
+ "changes",
+ "coalesce",
+ "glob",
+ "ifnull",
+ "hex",
+ "last_insert_rowid",
+ "length",
+ "like",
+ "lower",
+ "ltrim",
+ "max",
+ "min",
+ "nullif",
+ "quote",
+ "random",
+ "randomblob",
+ "replace",
+ "round",
+ "rtrim",
+ "soundex",
+ "total_change",
+ "trim",
+ "typeof",
+ "upper",
+ "zeroblob",
+ "date",
+ "datetime",
+ "julianday",
+ "strftime",
+ "avg",
+ "count",
+ "group_concat",
+ "sum",
+ "total",
]
# constants
@@ -52,4 +165,8 @@
def getSqlDictionary():
- return {'keyword': list(keywords), 'constant': list(constants), 'function': list(functions)}
+ return {
+ "keyword": list(keywords),
+ "constant": list(constants),
+ "function": list(functions),
+ }
diff --git a/python/plugins/db_manager/sqledit.py b/python/plugins/db_manager/sqledit.py
index 9b9067d6e4c1..deb78274ff77 100644
--- a/python/plugins/db_manager/sqledit.py
+++ b/python/plugins/db_manager/sqledit.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Alexander Bruy'
-__date__ = 'February 2014'
-__copyright__ = '(C) 2014, Alexander Bruy'
+__author__ = "Alexander Bruy"
+__date__ = "February 2014"
+__copyright__ = "(C) 2014, Alexander Bruy"
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QColor, QFont, QKeySequence
@@ -45,7 +45,7 @@ def setCommonOptions(self):
# Default font
font = QFont()
- font.setFamily('Courier')
+ font.setFamily("Courier")
font.setFixedPitch(True)
font.setPointSize(10)
self.setFont(font)
@@ -54,30 +54,33 @@ def setCommonOptions(self):
self.setBraceMatching(QsciScintilla.BraceMatch.SloppyBraceMatch)
self.setWrapMode(QsciScintilla.WrapMode.WrapWord)
- self.setWrapVisualFlags(QsciScintilla.WrapVisualFlag.WrapFlagByText,
- QsciScintilla.WrapVisualFlag.WrapFlagNone, 4)
+ self.setWrapVisualFlags(
+ QsciScintilla.WrapVisualFlag.WrapFlagByText,
+ QsciScintilla.WrapVisualFlag.WrapFlagNone,
+ 4,
+ )
- self.setSelectionForegroundColor(QColor('#2e3436'))
- self.setSelectionBackgroundColor(QColor('#babdb6'))
+ self.setSelectionForegroundColor(QColor("#2e3436"))
+ self.setSelectionBackgroundColor(QColor("#babdb6"))
# Show line numbers
- self.setMarginWidth(1, '000')
+ self.setMarginWidth(1, "000")
self.setMarginLineNumbers(1, True)
- self.setMarginsForegroundColor(QColor('#2e3436'))
- self.setMarginsBackgroundColor(QColor('#babdb6'))
+ self.setMarginsForegroundColor(QColor("#2e3436"))
+ self.setMarginsBackgroundColor(QColor("#babdb6"))
# Highlight current line
self.setCaretLineVisible(True)
- self.setCaretLineBackgroundColor(QColor('#d3d7cf'))
+ self.setCaretLineBackgroundColor(QColor("#d3d7cf"))
# Folding
self.setFolding(QsciScintilla.FoldStyle.BoxedTreeFoldStyle)
- self.setFoldMarginColors(QColor('#d3d7cf'), QColor('#d3d7cf'))
+ self.setFoldMarginColors(QColor("#d3d7cf"), QColor("#d3d7cf"))
# Mark column 80 with vertical line
self.setEdgeMode(QsciScintilla.EdgeMode.EdgeLine)
self.setEdgeColumn(80)
- self.setEdgeColor(QColor('#eeeeec'))
+ self.setEdgeColor(QColor("#eeeeec"))
# Indentation
self.setAutoIndent(True)
@@ -94,8 +97,8 @@ def setCommonOptions(self):
# Load font from Python console settings
settings = QgsSettings()
- fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace')
- fontSize = int(settings.value('pythonConsole/fontsize', 10))
+ fontName = settings.value("pythonConsole/fontfamilytext", "Monospace")
+ fontSize = int(settings.value("pythonConsole/fontsize", 10))
self.defaultFont = QFont(fontName)
self.defaultFont.setFixedPitch(True)
@@ -118,18 +121,18 @@ def initShortcuts(self):
(ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16)
# Disable some shortcuts
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl +
- shift)
- self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("D") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl + shift)
+ self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("T") + ctrl)
# self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl)
# self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl)
# Use Ctrl+Space for autocompletion
- self.shortcutAutocomplete = QShortcut(QKeySequence(Qt.Modifier.CTRL +
- Qt.Key.Key_Space), self)
+ self.shortcutAutocomplete = QShortcut(
+ QKeySequence(Qt.Modifier.CTRL + Qt.Key.Key_Space), self
+ )
self.shortcutAutocomplete.setContext(Qt.ShortcutContext.WidgetShortcut)
self.shortcutAutocomplete.activated.connect(self.autoComplete)
@@ -139,13 +142,13 @@ def autoComplete(self):
def initLexer(self):
self.mylexer = QsciLexerSQL()
- colorDefault = QColor('#2e3436')
- colorComment = QColor('#c00')
- colorCommentBlock = QColor('#3465a4')
- colorNumber = QColor('#4e9a06')
- colorType = QColor('#4e9a06')
- colorKeyword = QColor('#204a87')
- colorString = QColor('#ce5c00')
+ colorDefault = QColor("#2e3436")
+ colorComment = QColor("#c00")
+ colorCommentBlock = QColor("#3465a4")
+ colorNumber = QColor("#4e9a06")
+ colorType = QColor("#4e9a06")
+ colorKeyword = QColor("#204a87")
+ colorString = QColor("#ce5c00")
self.mylexer.setDefaultFont(self.defaultFont)
self.mylexer.setDefaultColor(colorDefault)
diff --git a/python/plugins/grassprovider/__init__.py b/python/plugins/grassprovider/__init__.py
index 9af45507fb6d..42e27a6b6e70 100644
--- a/python/plugins/grassprovider/__init__.py
+++ b/python/plugins/grassprovider/__init__.py
@@ -15,11 +15,12 @@
***************************************************************************
"""
-__author__ = 'Alexander Bruy'
-__date__ = 'June 2021'
-__copyright__ = '(C) 2021, Alexander Bruy'
+__author__ = "Alexander Bruy"
+__date__ = "June 2021"
+__copyright__ = "(C) 2021, Alexander Bruy"
def classFactory(iface):
from grassprovider.grass_plugin import GrassProviderPlugin
+
return GrassProviderPlugin()
diff --git a/python/plugins/grassprovider/description_to_json.py b/python/plugins/grassprovider/description_to_json.py
index 08038d600c62..d90067f0b0d6 100644
--- a/python/plugins/grassprovider/description_to_json.py
+++ b/python/plugins/grassprovider/description_to_json.py
@@ -19,32 +19,34 @@
def main(description_folder: str, output_file: str):
- from .parsed_description import (
- ParsedDescription
- )
+ from .parsed_description import ParsedDescription
algorithms = []
folder = Path(description_folder)
- for description_file in folder.glob('*.txt'):
+ for description_file in folder.glob("*.txt"):
description = ParsedDescription.parse_description_file(
- description_file, translate=False)
+ description_file, translate=False
+ )
ext_path = description_file.parents[1].joinpath(
- 'ext', description.name.replace('.', '_') + '.py')
+ "ext", description.name.replace(".", "_") + ".py"
+ )
if ext_path.exists():
- description.ext_path = description.name.replace('.', '_')
+ description.ext_path = description.name.replace(".", "_")
algorithms.append(description.as_dict())
Path(output_file).parent.mkdir(parents=True, exist_ok=True)
- with open(output_file, 'wt', encoding='utf8') as f_out:
+ with open(output_file, "w", encoding="utf8") as f_out:
f_out.write(json.dumps(algorithms, indent=2))
-parser = argparse.ArgumentParser(description="Parses GRASS .txt algorithm "
- "description files and builds "
- "aggregated .json description")
+parser = argparse.ArgumentParser(
+ description="Parses GRASS .txt algorithm "
+ "description files and builds "
+ "aggregated .json description"
+)
parser.add_argument("input", help="Path to the description directory")
parser.add_argument("output", help="Path to the output algorithms.json file")
diff --git a/python/plugins/grassprovider/ext/g_extension_list.py b/python/plugins/grassprovider/ext/g_extension_list.py
index 263a173d9b47..9024e98c4d88 100644
--- a/python/plugins/grassprovider/ext/g_extension_list.py
+++ b/python/plugins/grassprovider/ext/g_extension_list.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Alister Hood'
-__date__ = 'May 2023'
-__copyright__ = '(C) 2023, Alister Hood'
+__author__ = "Alister Hood"
+__date__ = "May 2023"
+__copyright__ = "(C) 2023, Alister Hood"
from qgis.core import QgsProcessingParameterBoolean
@@ -25,26 +25,26 @@
def processInputs(alg, parameters, context, feedback):
# get the option we want to run
# we would need to update the if statements below if the order in the description file is changed
- selectedOption = alg.parameterAsInt(parameters, 'list', context)
+ selectedOption = alg.parameterAsInt(parameters, "list", context)
# Locally installed extensions
if selectedOption == 0:
- optionString = '-a'
+ optionString = "-a"
# Extensions available in the official GRASS GIS Addons repository
elif selectedOption == 1:
- optionString = '-l'
+ optionString = "-l"
# Extensions available in the official GRASS GIS Addons repository including module description
else:
- optionString = '-c'
- param = QgsProcessingParameterBoolean(optionString, '', True)
+ optionString = "-c"
+ param = QgsProcessingParameterBoolean(optionString, "", True)
alg.addParameter(param)
# Don't forget to remove the old input parameter
- alg.removeParameter('list')
+ alg.removeParameter("list")
def convertToHtml(alg, fileName, outputs):
# don't copy this for something that will have lots of data like r.stats
# it will be very slow
- with open(fileName, 'r') as f:
- outputs['GRASS_ADDONS'] = f.read()
+ with open(fileName) as f:
+ outputs["GRASS_ADDONS"] = f.read()
# this will read the file a second time, but calling it saves us duplicating the code here
alg.convertToHtml(fileName)
diff --git a/python/plugins/grassprovider/ext/g_extension_manage.py b/python/plugins/grassprovider/ext/g_extension_manage.py
index 51893b1de21d..d69cd49d7dfe 100644
--- a/python/plugins/grassprovider/ext/g_extension_manage.py
+++ b/python/plugins/grassprovider/ext/g_extension_manage.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Alister Hood'
-__date__ = 'May 2023'
-__copyright__ = '(C) 2023, Alister Hood'
+__author__ = "Alister Hood"
+__date__ = "May 2023"
+__copyright__ = "(C) 2023, Alister Hood"
def processInputs(alg, parameters, context, feedback):
diff --git a/python/plugins/grassprovider/ext/g_version.py b/python/plugins/grassprovider/ext/g_version.py
index 6fbd09ffc1f2..3d44d5102e04 100644
--- a/python/plugins/grassprovider/ext/g_version.py
+++ b/python/plugins/grassprovider/ext/g_version.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Alister Hood'
-__date__ = 'May 2023'
-__copyright__ = '(C) 2023, Alister Hood'
+__author__ = "Alister Hood"
+__date__ = "May 2023"
+__copyright__ = "(C) 2023, Alister Hood"
def processInputs(alg, parameters, context, feedback):
@@ -27,7 +27,7 @@ def processInputs(alg, parameters, context, feedback):
def convertToHtml(alg, fileName, outputs):
# don't copy this for something that will have lots of data like r.stats
# it will be very slow
- with open(fileName, 'r') as f:
- outputs['GRASS_VERSIONINFO'] = f.read()
+ with open(fileName) as f:
+ outputs["GRASS_VERSIONINFO"] = f.read()
# this will read the file a second time, but calling it saves us duplicating the code here
alg.convertToHtml(fileName)
diff --git a/python/plugins/grassprovider/ext/i.py b/python/plugins/grassprovider/ext/i.py
index 226eda04fc1d..51225470445f 100644
--- a/python/plugins/grassprovider/ext/i.py
+++ b/python/plugins/grassprovider/ext/i.py
@@ -15,12 +15,12 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'April 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "April 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
-from processing.tools.system import (isWindows, getTempFilename)
+from processing.tools.system import isWindows, getTempFilename
from grassprovider.grass_utils import GrassUtils
from qgis.PyQt.QtCore import QDir
from qgis.core import QgsProcessingParameterString
@@ -36,10 +36,11 @@ def orderedInput(alg, parameters, context, src, tgt, numSeq=None):
:param tgt: Name of a new input parameter.
:param numSeq: List of a sequence for naming layers.
"""
- rootFilename = 'rast_{}.'.format(os.path.basename(getTempFilename(context=context)))
+ rootFilename = f"rast_{os.path.basename(getTempFilename(context=context))}."
# parameters[tgt] = rootFilename
- param = QgsProcessingParameterString(tgt, 'virtual input',
- rootFilename, False, False)
+ param = QgsProcessingParameterString(
+ tgt, "virtual input", rootFilename, False, False
+ )
alg.addParameter(param)
rasters = alg.parameterAsLayerList(parameters, src, context)
@@ -48,7 +49,7 @@ def orderedInput(alg, parameters, context, src, tgt, numSeq=None):
numSeq = list(range(1, len(rasters) + 1))
for idx, raster in enumerate(rasters):
- rasterName = '{}{}'.format(rootFilename, numSeq[idx])
+ rasterName = f"{rootFilename}{numSeq[idx]}"
alg.loadRasterLayer(rasterName, raster, context, False, None, rasterName)
# Don't forget to remove the old input parameter
@@ -69,32 +70,35 @@ def regroupRasters(alg, parameters, context, src, group, subgroup=None, extFile=
:param extFile: dict : parameterName:directory name
"""
# Create a group parameter
- groupName = 'group_{}'.format(os.path.basename(getTempFilename(context=context)))
- param = QgsProcessingParameterString(group, 'virtual group',
- groupName, False, False)
+ groupName = f"group_{os.path.basename(getTempFilename(context=context))}"
+ param = QgsProcessingParameterString(
+ group, "virtual group", groupName, False, False
+ )
alg.addParameter(param)
# Create a subgroup
subgroupName = None
if subgroup:
- subgroupName = 'subgroup_{}'.format(os.path.basename(getTempFilename(context=context)))
- param = QgsProcessingParameterString(subgroup, 'virtual subgroup',
- subgroupName, False, False)
+ subgroupName = f"subgroup_{os.path.basename(getTempFilename(context=context))}"
+ param = QgsProcessingParameterString(
+ subgroup, "virtual subgroup", subgroupName, False, False
+ )
alg.addParameter(param)
# Compute raster names
rasters = alg.parameterAsLayerList(parameters, src, context)
rasterNames = []
for idx, raster in enumerate(rasters):
- name = '{}_{}'.format(src, idx)
+ name = f"{src}_{idx}"
if name in alg.exportedLayers:
rasterNames.append(alg.exportedLayers[name])
# Insert a i.group command
- command = 'i.group group={}{} input={}'.format(
+ command = "i.group group={}{} input={}".format(
groupName,
- ' subgroup={}'.format(subgroupName) if subgroup else '',
- ','.join(rasterNames))
+ f" subgroup={subgroupName}" if subgroup else "",
+ ",".join(rasterNames),
+ )
alg.commands.append(command)
# Handle external files
@@ -115,35 +119,54 @@ def regroupRasters(alg, parameters, context, src, group, subgroup=None, extFile=
return groupName, subgroupName
-def importSigFile(alg, group, subgroup, src, sigDir='sig'):
+def importSigFile(alg, group, subgroup, src, sigDir="sig"):
"""
Import a signature file into an
internal GRASSDB folder
"""
shortSigFile = os.path.basename(src)
- interSig = os.path.join(GrassUtils.grassMapsetFolder(),
- 'PERMANENT', 'group', group, 'subgroup',
- subgroup, sigDir, shortSigFile)
+ interSig = os.path.join(
+ GrassUtils.grassMapsetFolder(),
+ "PERMANENT",
+ "group",
+ group,
+ "subgroup",
+ subgroup,
+ sigDir,
+ shortSigFile,
+ )
copyFile(alg, src, interSig)
return shortSigFile
-def exportSigFile(alg, group, subgroup, dest, sigDir='sig'):
+def exportSigFile(alg, group, subgroup, dest, sigDir="sig"):
"""
Export a signature file from internal GRASSDB
to final destination
"""
shortSigFile = os.path.basename(dest)
- grass_version = int(GrassUtils.installedVersion().split('.')[0])
+ grass_version = int(GrassUtils.installedVersion().split(".")[0])
if grass_version >= 8:
- interSig = os.path.join(GrassUtils.grassMapsetFolder(),
- 'PERMANENT', 'signatures',
- sigDir, shortSigFile, 'sig')
+ interSig = os.path.join(
+ GrassUtils.grassMapsetFolder(),
+ "PERMANENT",
+ "signatures",
+ sigDir,
+ shortSigFile,
+ "sig",
+ )
else:
- interSig = os.path.join(GrassUtils.grassMapsetFolder(),
- 'PERMANENT', 'group', group, 'subgroup',
- subgroup, sigDir, shortSigFile)
+ interSig = os.path.join(
+ GrassUtils.grassMapsetFolder(),
+ "PERMANENT",
+ "group",
+ group,
+ "subgroup",
+ subgroup,
+ sigDir,
+ shortSigFile,
+ )
moveFile(alg, interSig, dest)
return interSig
@@ -160,7 +183,8 @@ def exportInputRasters(alg, parameters, context, rasterDic):
# Get inputs and outputs
for inputName, outputName in rasterDic.items():
fileName = os.path.normpath(
- alg.parameterAsOutputLayer(parameters, outputName, context))
+ alg.parameterAsOutputLayer(parameters, outputName, context)
+ )
grassName = alg.exportedLayers[inputName]
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
@@ -170,9 +194,15 @@ def verifyRasterNum(alg, parameters, context, rasters, mini, maxi=None):
"""Verify that we have at least n rasters in multipleInput"""
num = len(alg.parameterAsLayerList(parameters, rasters, context))
if num < mini:
- return False, 'You need to set at least {} input rasters for this algorithm!'.format(mini)
+ return (
+ False,
+ f"You need to set at least {mini} input rasters for this algorithm!",
+ )
if maxi and num > maxi:
- return False, 'You need to set a maximum of {} input rasters for this algorithm!'.format(maxi)
+ return (
+ False,
+ f"You need to set a maximum of {maxi} input rasters for this algorithm!",
+ )
return True, None
@@ -191,31 +221,32 @@ def verifyRasterNum(alg, parameters, context, rasters, mini, maxi=None):
def createDestDir(alg, toFile):
- """ Generates an mkdir command for GRASS script """
+ """Generates an mkdir command for GRASS script"""
# Creates the destination directory
- command = "{} \"{}\"".format(
+ command = '{} "{}"'.format(
"MD" if isWindows() else "mkdir -p",
- QDir.toNativeSeparators(os.path.dirname(toFile))
+ QDir.toNativeSeparators(os.path.dirname(toFile)),
)
alg.commands.append(command)
def moveFile(alg, fromFile, toFile):
- """ Generates a move command for GRASS script """
+ """Generates a move command for GRASS script"""
createDestDir(alg, toFile)
- command = "{} \"{}\" \"{}\"".format(
+ command = '{} "{}" "{}"'.format(
"MOVE /Y" if isWindows() else "mv -f",
QDir.toNativeSeparators(fromFile),
- QDir.toNativeSeparators(toFile)
+ QDir.toNativeSeparators(toFile),
)
alg.commands.append(command)
def copyFile(alg, fromFile, toFile):
- """ Generates a copy command for GRASS script """
+ """Generates a copy command for GRASS script"""
createDestDir(alg, toFile)
- command = "{} \"{}\" \"{}\"".format(
+ command = '{} "{}" "{}"'.format(
"COPY /Y" if isWindows() else "cp -f",
QDir.toNativeSeparators(fromFile),
- QDir.toNativeSeparators(toFile))
+ QDir.toNativeSeparators(toFile),
+ )
alg.commands.append(command)
diff --git a/python/plugins/grassprovider/ext/i_albedo.py b/python/plugins/grassprovider/ext/i_albedo.py
index ab418d15a01f..2dda8d334e5c 100644
--- a/python/plugins/grassprovider/ext/i_albedo.py
+++ b/python/plugins/grassprovider/ext/i_albedo.py
@@ -15,19 +15,20 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- if alg.parameterAsBoolean(parameters, '-m', context):
- return verifyRasterNum(alg, parameters, context, 'input', 7)
- elif alg.parameterAsBoolean(parameters, '-n', context):
- return verifyRasterNum(alg, parameters, context, 'input', 2)
- elif (alg.parameterAsBoolean(parameters, '-l', context)
- or alg.parameterAsBoolean(parameters, '-a', context)):
- return verifyRasterNum(alg, parameters, context, 'input', 6)
+ if alg.parameterAsBoolean(parameters, "-m", context):
+ return verifyRasterNum(alg, parameters, context, "input", 7)
+ elif alg.parameterAsBoolean(parameters, "-n", context):
+ return verifyRasterNum(alg, parameters, context, "input", 2)
+ elif alg.parameterAsBoolean(parameters, "-l", context) or alg.parameterAsBoolean(
+ parameters, "-a", context
+ ):
+ return verifyRasterNum(alg, parameters, context, "input", 6)
return True, None
diff --git a/python/plugins/grassprovider/ext/i_cca.py b/python/plugins/grassprovider/ext/i_cca.py
index 97e3acd99f31..61afff682146 100644
--- a/python/plugins/grassprovider/ext/i_cca.py
+++ b/python/plugins/grassprovider/ext/i_cca.py
@@ -15,25 +15,26 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum, regroupRasters, importSigFile
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 2, 8)
+ return verifyRasterNum(alg, parameters, context, "input", 2, 8)
def processCommand(alg, parameters, context, feedback):
# Regroup rasters
- group, subgroup = regroupRasters(alg, parameters, context,
- 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
- signatureFile = alg.parameterAsString(parameters, 'signature', context)
+ signatureFile = alg.parameterAsString(parameters, "signature", context)
shortSigFile = importSigFile(alg, group, subgroup, signatureFile)
- parameters['signature'] = shortSigFile
+ parameters["signature"] = shortSigFile
# Handle other parameters
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/i_cluster.py b/python/plugins/grassprovider/ext/i_cluster.py
index ac7e1cbdb7e3..950e14d3e7f7 100644
--- a/python/plugins/grassprovider/ext/i_cluster.py
+++ b/python/plugins/grassprovider/ext/i_cluster.py
@@ -15,31 +15,33 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from .i import regroupRasters, verifyRasterNum, exportSigFile
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 2)
+ return verifyRasterNum(alg, parameters, context, "input", 2)
def processCommand(alg, parameters, context, feedback):
# We need to extract the basename of the signature file
- signatureFile = alg.parameterAsString(parameters, 'signaturefile', context)
+ signatureFile = alg.parameterAsString(parameters, "signaturefile", context)
shortSigFile = os.path.basename(signatureFile)
- parameters['signaturefile'] = shortSigFile
+ parameters["signaturefile"] = shortSigFile
# Regroup rasters
- group, subgroup = regroupRasters(alg, parameters, context, 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
alg.processCommand(parameters, context, feedback)
# Re-add signature files
- parameters['signaturefile'] = signatureFile
- alg.fileOutputs['signaturefile'] = signatureFile
+ parameters["signaturefile"] = signatureFile
+ alg.fileOutputs["signaturefile"] = signatureFile
# Export signature file
exportSigFile(alg, group, subgroup, signatureFile)
diff --git a/python/plugins/grassprovider/ext/i_colors_enhance.py b/python/plugins/grassprovider/ext/i_colors_enhance.py
index 9fd9df1beedf..15c3d7a3ec57 100644
--- a/python/plugins/grassprovider/ext/i_colors_enhance.py
+++ b/python/plugins/grassprovider/ext/i_colors_enhance.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import exportInputRasters
@@ -29,5 +29,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# Input rasters are output rasters
- rasterDic = {'red': 'redoutput', 'green': 'greenoutput', 'blue': 'blueoutput'}
+ rasterDic = {"red": "redoutput", "green": "greenoutput", "blue": "blueoutput"}
exportInputRasters(alg, parameters, context, rasterDic)
diff --git a/python/plugins/grassprovider/ext/i_evapo_mh.py b/python/plugins/grassprovider/ext/i_evapo_mh.py
index 101f796d0047..761eebcae038 100644
--- a/python/plugins/grassprovider/ext/i_evapo_mh.py
+++ b/python/plugins/grassprovider/ext/i_evapo_mh.py
@@ -15,16 +15,22 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- if (alg.parameterAsBoolean(parameters, '-h', context)
- and alg.parameterAsLayer(parameters, 'precipitation', context)):
- return False, alg.tr('You can\'t use original Hargreaves flag and precipitation parameter together!')
- if (not alg.parameterAsBoolean(parameters, '-h', context)
- and not alg.parameterAsLayer(parameters, 'precipitation', context)):
- return False, alg.tr('If you don\'t use original Hargreaves flag, you must set the precipitation raster parameter!')
+ if alg.parameterAsBoolean(parameters, "-h", context) and alg.parameterAsLayer(
+ parameters, "precipitation", context
+ ):
+ return False, alg.tr(
+ "You can't use original Hargreaves flag and precipitation parameter together!"
+ )
+ if not alg.parameterAsBoolean(
+ parameters, "-h", context
+ ) and not alg.parameterAsLayer(parameters, "precipitation", context):
+ return False, alg.tr(
+ "If you don't use original Hargreaves flag, you must set the precipitation raster parameter!"
+ )
return True, None
diff --git a/python/plugins/grassprovider/ext/i_gensig.py b/python/plugins/grassprovider/ext/i_gensig.py
index db67d2da4781..02ab5d6b5cba 100644
--- a/python/plugins/grassprovider/ext/i_gensig.py
+++ b/python/plugins/grassprovider/ext/i_gensig.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from .i import regroupRasters, exportSigFile
@@ -25,17 +25,19 @@
def processCommand(alg, parameters, context, feedback):
# We need to extract the basename of the signature file
- signatureFile = alg.parameterAsString(parameters, 'signaturefile', context)
+ signatureFile = alg.parameterAsString(parameters, "signaturefile", context)
shortSigFile = os.path.basename(signatureFile)
- parameters['signaturefile'] = shortSigFile
+ parameters["signaturefile"] = shortSigFile
# Regroup rasters
- group, subgroup = regroupRasters(alg, parameters, context, 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
alg.processCommand(parameters, context, feedback)
# Re-add signature files
- parameters['signaturefile'] = signatureFile
- alg.fileOutputs['signaturefile'] = signatureFile
+ parameters["signaturefile"] = signatureFile
+ alg.fileOutputs["signaturefile"] = signatureFile
# Export signature file
exportSigFile(alg, group, subgroup, signatureFile)
diff --git a/python/plugins/grassprovider/ext/i_gensigset.py b/python/plugins/grassprovider/ext/i_gensigset.py
index c378f3550d2b..e233d30484cd 100644
--- a/python/plugins/grassprovider/ext/i_gensigset.py
+++ b/python/plugins/grassprovider/ext/i_gensigset.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from .i import regroupRasters, exportSigFile
@@ -25,17 +25,19 @@
def processCommand(alg, parameters, context, feedback):
# We need to extract the basename of the signature file
- signatureFile = alg.parameterAsString(parameters, 'signaturefile', context)
+ signatureFile = alg.parameterAsString(parameters, "signaturefile", context)
shortSigFile = os.path.basename(signatureFile)
- parameters['signaturefile'] = shortSigFile
+ parameters["signaturefile"] = shortSigFile
# Regroup rasters
- group, subgroup = regroupRasters(alg, parameters, context, 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
alg.processCommand(parameters, context, feedback)
# Re-add signature files
- parameters['signaturefile'] = signatureFile
- alg.fileOutputs['signaturefile'] = signatureFile
+ parameters["signaturefile"] = signatureFile
+ alg.fileOutputs["signaturefile"] = signatureFile
# Export signature file
- exportSigFile(alg, group, subgroup, signatureFile, 'sigset')
+ exportSigFile(alg, group, subgroup, signatureFile, "sigset")
diff --git a/python/plugins/grassprovider/ext/i_group.py b/python/plugins/grassprovider/ext/i_group.py
index ff52ac71aebf..325039ed80e3 100644
--- a/python/plugins/grassprovider/ext/i_group.py
+++ b/python/plugins/grassprovider/ext/i_group.py
@@ -15,12 +15,12 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 2)
+ return verifyRasterNum(alg, parameters, context, "input", 2)
diff --git a/python/plugins/grassprovider/ext/i_in_spotvgt.py b/python/plugins/grassprovider/ext/i_in_spotvgt.py
index 0c6accf7f414..32b028e1271f 100644
--- a/python/plugins/grassprovider/ext/i_in_spotvgt.py
+++ b/python/plugins/grassprovider/ext/i_in_spotvgt.py
@@ -15,13 +15,13 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'April 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "April 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
# Here, we apply directly the algorithm
# So we just need to get the projection of the layer !
- layer = alg.parameterAsRasterLayer(parameters, 'input', context)
+ layer = alg.parameterAsRasterLayer(parameters, "input", context)
alg.setSessionProjectionFromLayer(layer, context)
diff --git a/python/plugins/grassprovider/ext/i_landsat_acca.py b/python/plugins/grassprovider/ext/i_landsat_acca.py
index 1b1729e07e9f..da74d16f7495 100644
--- a/python/plugins/grassprovider/ext/i_landsat_acca.py
+++ b/python/plugins/grassprovider/ext/i_landsat_acca.py
@@ -15,20 +15,19 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum, orderedInput
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'rasters', 5, 5)
+ return verifyRasterNum(alg, parameters, context, "rasters", 5, 5)
def processInputs(alg, parameters, context, feedback):
- orderedInput(alg, parameters, context, 'rasters', 'input',
- [2, 3, 4, 5, 61])
+ orderedInput(alg, parameters, context, "rasters", "input", [2, 3, 4, 5, 61])
def processCommand(alg, parameters, context, feedback):
diff --git a/python/plugins/grassprovider/ext/i_landsat_toar.py b/python/plugins/grassprovider/ext/i_landsat_toar.py
index d4b1509dc9f1..9b231091761a 100644
--- a/python/plugins/grassprovider/ext/i_landsat_toar.py
+++ b/python/plugins/grassprovider/ext/i_landsat_toar.py
@@ -15,20 +15,21 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum, orderedInput
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'rasters', 5, 12)
+ return verifyRasterNum(alg, parameters, context, "rasters", 5, 12)
def processInputs(alg, parameters, context, feedback):
- orderedInput(alg, parameters, context, 'rasters', 'input',
- [1, 2, 3, 4, 5, 61, 62, 7, 8])
+ orderedInput(
+ alg, parameters, context, "rasters", "input", [1, 2, 3, 4, 5, 61, 62, 7, 8]
+ )
def processCommand(alg, parameters, context, feedback):
diff --git a/python/plugins/grassprovider/ext/i_maxlik.py b/python/plugins/grassprovider/ext/i_maxlik.py
index 9e134d5f85c8..a2043eafa183 100644
--- a/python/plugins/grassprovider/ext/i_maxlik.py
+++ b/python/plugins/grassprovider/ext/i_maxlik.py
@@ -15,21 +15,22 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import regroupRasters, importSigFile
def processCommand(alg, parameters, context, feedback):
- group, subgroup = regroupRasters(alg, parameters, context,
- 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
# import signature
- signatureFile = alg.parameterAsString(parameters, 'signaturefile', context)
+ signatureFile = alg.parameterAsString(parameters, "signaturefile", context)
shortSigFile = importSigFile(alg, group, subgroup, signatureFile)
- parameters['signaturefile'] = shortSigFile
+ parameters["signaturefile"] = shortSigFile
# Handle other parameters
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/i_oif.py b/python/plugins/grassprovider/ext/i_oif.py
index 6249a931e1bc..e99925ef513d 100644
--- a/python/plugins/grassprovider/ext/i_oif.py
+++ b/python/plugins/grassprovider/ext/i_oif.py
@@ -15,12 +15,12 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 4)
+ return verifyRasterNum(alg, parameters, context, "input", 4)
diff --git a/python/plugins/grassprovider/ext/i_pansharpen.py b/python/plugins/grassprovider/ext/i_pansharpen.py
index 056d3e32361f..05e436e2805b 100644
--- a/python/plugins/grassprovider/ext/i_pansharpen.py
+++ b/python/plugins/grassprovider/ext/i_pansharpen.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from qgis.core import QgsProcessingParameterString
@@ -27,19 +27,20 @@
def processCommand(alg, parameters, context, feedback):
# Temporary remove outputs and add a virtual output parameter
- outputName = 'output_{}'.format(os.path.basename(getTempFilename(context=context)))
- param = QgsProcessingParameterString('output', 'virtual output',
- outputName, False, False)
+ outputName = f"output_{os.path.basename(getTempFilename(context=context))}"
+ param = QgsProcessingParameterString(
+ "output", "virtual output", outputName, False, False
+ )
alg.addParameter(param)
alg.processCommand(parameters, context, feedback, True)
def processOutputs(alg, parameters, context, feedback):
- outputName = alg.parameterAsString(parameters, 'output', context)
+ outputName = alg.parameterAsString(parameters, "output", context)
createOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_OPT, context)
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
- for channel in ['red', 'green', 'blue']:
- fileName = alg.parameterAsOutputLayer(parameters, '{}output'.format(channel), context)
- grassName = '{}_{}'.format(outputName, channel)
+ for channel in ["red", "green", "blue"]:
+ fileName = alg.parameterAsOutputLayer(parameters, f"{channel}output", context)
+ grassName = f"{outputName}_{channel}"
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/i_pca.py b/python/plugins/grassprovider/ext/i_pca.py
index 89af12f8a34f..6a4b55586f2b 100644
--- a/python/plugins/grassprovider/ext/i_pca.py
+++ b/python/plugins/grassprovider/ext/i_pca.py
@@ -15,12 +15,12 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 2)
+ return verifyRasterNum(alg, parameters, context, "input", 2)
diff --git a/python/plugins/grassprovider/ext/i_segment.py b/python/plugins/grassprovider/ext/i_segment.py
index 19f676ee236a..c19c6cf9f358 100644
--- a/python/plugins/grassprovider/ext/i_segment.py
+++ b/python/plugins/grassprovider/ext/i_segment.py
@@ -15,14 +15,14 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import regroupRasters
def processCommand(alg, parameters, context, feedback):
# Regroup rasters
- regroupRasters(alg, parameters, context, 'input', 'group')
+ regroupRasters(alg, parameters, context, "input", "group")
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/i_smap.py b/python/plugins/grassprovider/ext/i_smap.py
index 310d234d66a0..e14535e411c3 100644
--- a/python/plugins/grassprovider/ext/i_smap.py
+++ b/python/plugins/grassprovider/ext/i_smap.py
@@ -15,21 +15,23 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import regroupRasters, importSigFile
def processCommand(alg, parameters, context, feedback):
# Regroup rasters
- group, subgroup = regroupRasters(alg, parameters, context, 'input', 'group', 'subgroup')
+ group, subgroup = regroupRasters(
+ alg, parameters, context, "input", "group", "subgroup"
+ )
# import signature
- signatureFile = alg.parameterAsString(parameters, 'signaturefile', context)
- shortSigFile = importSigFile(alg, group, subgroup, signatureFile, 'sigset')
- parameters['signaturefile'] = shortSigFile
+ signatureFile = alg.parameterAsString(parameters, "signaturefile", context)
+ shortSigFile = importSigFile(alg, group, subgroup, signatureFile, "sigset")
+ parameters["signaturefile"] = shortSigFile
# Handle other parameters
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/i_tasscap.py b/python/plugins/grassprovider/ext/i_tasscap.py
index eda544125acf..831fd959396f 100644
--- a/python/plugins/grassprovider/ext/i_tasscap.py
+++ b/python/plugins/grassprovider/ext/i_tasscap.py
@@ -15,12 +15,12 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .i import verifyRasterNum
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- return verifyRasterNum(alg, parameters, context, 'input', 6, 8)
+ return verifyRasterNum(alg, parameters, context, "input", 6, 8)
diff --git a/python/plugins/grassprovider/ext/r_blend_combine.py b/python/plugins/grassprovider/ext/r_blend_combine.py
index 4608a8360594..5d7628256bb7 100644
--- a/python/plugins/grassprovider/ext/r_blend_combine.py
+++ b/python/plugins/grassprovider/ext/r_blend_combine.py
@@ -15,21 +15,21 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
- if 'first' and 'second' in alg.exportedLayers:
+ if "first" and "second" in alg.exportedLayers:
return
# Use v.in.ogr
- for name in ['first', 'second']:
+ for name in ["first", "second"]:
alg.loadRasterLayerFromParameter(name, parameters, context, False, None)
alg.postInputs(context)
def processOutputs(alg, parameters, context, feedback):
# Keep color table
- alg.exportRasterLayerFromParameter('output', parameters, context)
+ alg.exportRasterLayerFromParameter("output", parameters, context)
diff --git a/python/plugins/grassprovider/ext/r_blend_rgb.py b/python/plugins/grassprovider/ext/r_blend_rgb.py
index 2e763d5412c1..9cfe11ce7f5b 100644
--- a/python/plugins/grassprovider/ext/r_blend_rgb.py
+++ b/python/plugins/grassprovider/ext/r_blend_rgb.py
@@ -15,20 +15,20 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from grassprovider.grass_utils import GrassUtils
def processInputs(alg, parameters, context, feedback):
- if 'first' and 'second' in alg.exportedLayers:
+ if "first" and "second" in alg.exportedLayers:
return
# Use v.in.ogr
- for name in ['first', 'second']:
+ for name in ["first", "second"]:
alg.loadRasterLayerFromParameter(name, parameters, context, False, None)
alg.postInputs(context)
@@ -43,10 +43,12 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# Export each color raster
- colors = ['red', 'green', 'blue']
+ colors = ["red", "green", "blue"]
for color in colors:
fileName = os.path.normpath(
- alg.parameterAsOutputLayer(parameters, 'output_{}'.format(color), context))
+ alg.parameterAsOutputLayer(parameters, f"output_{color}", context)
+ )
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- alg.exportRasterLayer('blended.{}'.format(color[0]),
- fileName, True, outFormat, createOpt, metaOpt)
+ alg.exportRasterLayer(
+ f"blended.{color[0]}", fileName, True, outFormat, createOpt, metaOpt
+ )
diff --git a/python/plugins/grassprovider/ext/r_category.py b/python/plugins/grassprovider/ext/r_category.py
index 1f9e0476468e..06c35e68be90 100644
--- a/python/plugins/grassprovider/ext/r_category.py
+++ b/python/plugins/grassprovider/ext/r_category.py
@@ -15,24 +15,28 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from processing.tools.system import getTempFilename
from grassprovider.grass_utils import GrassUtils
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- rules = alg.parameterAsString(parameters, 'rules', context)
- txtrules = alg.parameterAsString(parameters, 'txtrules', context)
- raster = alg.parameterAsString(parameters, 'raster', context)
+ """Verify if we have the right parameters"""
+ rules = alg.parameterAsString(parameters, "rules", context)
+ txtrules = alg.parameterAsString(parameters, "txtrules", context)
+ raster = alg.parameterAsString(parameters, "raster", context)
if rules and txtrules:
- return False, alg.tr("You need to set either a rules file or write directly the rules!")
+ return False, alg.tr(
+ "You need to set either a rules file or write directly the rules!"
+ )
elif (rules and raster) or (txtrules and raster):
- return False, alg.tr("You need to set either rules or a raster from which to copy categories!")
+ return False, alg.tr(
+ "You need to set either rules or a raster from which to copy categories!"
+ )
return True, None
@@ -40,18 +44,16 @@ def checkParameterValuesBeforeExecuting(alg, parameters, context):
def processInputs(alg, parameters, context, feedback):
# If there is another raster to copy categories from
# we need to import it with r.in.gdal rather than r.external
- raster = alg.parameterAsString(parameters, 'raster', context)
+ raster = alg.parameterAsString(parameters, "raster", context)
if raster:
- alg.loadRasterLayerFromParameter('raster',
- parameters, context,
- False, None)
- alg.loadRasterLayerFromParameter('map', parameters, context)
+ alg.loadRasterLayerFromParameter("raster", parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("map", parameters, context)
alg.postInputs(context)
def processCommand(alg, parameters, context, feedback):
# Handle inline rules
- txtRules = alg.parameterAsString(parameters, 'txtrules', context)
+ txtRules = alg.parameterAsString(parameters, "txtrules", context)
if txtRules:
# Creates a temporary txt file
tempRulesName = getTempFilename(context=context)
@@ -59,8 +61,8 @@ def processCommand(alg, parameters, context, feedback):
# Inject rules into temporary txt file
with open(tempRulesName, "w") as tempRules:
tempRules.write(txtRules)
- alg.removeParameter('txtrules')
- parameters['rules'] = tempRulesName
+ alg.removeParameter("txtrules")
+ parameters["rules"] = tempRulesName
alg.processCommand(parameters, context, feedback, True)
@@ -71,8 +73,7 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# We need to export the raster with all its bands and its color table
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- grassName = alg.exportedLayers['map']
- alg.exportRasterLayer(grassName, fileName, True,
- outFormat, createOpt, metaOpt)
+ grassName = alg.exportedLayers["map"]
+ alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_colors.py b/python/plugins/grassprovider/ext/r_colors.py
index 3ec6f0ea9abc..3df349d6b99c 100644
--- a/python/plugins/grassprovider/ext/r_colors.py
+++ b/python/plugins/grassprovider/ext/r_colors.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from grassprovider.grass_utils import GrassUtils
@@ -25,17 +25,19 @@
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- txtRules = alg.parameterAsString(parameters, 'rules_txt', context)
- rules = alg.parameterAsString(parameters, 'rules', context)
+ """Verify if we have the right parameters"""
+ txtRules = alg.parameterAsString(parameters, "rules_txt", context)
+ rules = alg.parameterAsString(parameters, "rules", context)
if txtRules and rules:
return False, alg.tr("You need to set either inline rules or a rules file!")
rules_or_txtRules = bool(txtRules or rules)
- color = bool(alg.parameterAsEnum(parameters, 'color', context))
- raster = bool(alg.parameterAsString(parameters, 'raster', context))
+ color = bool(alg.parameterAsEnum(parameters, "color", context))
+ raster = bool(alg.parameterAsString(parameters, "raster", context))
if not sum([rules_or_txtRules, color, raster]) == 1:
- return False, alg.tr("The color table, color rules and raster map parameters are mutually exclusive. You need to set one and only one of them!")
+ return False, alg.tr(
+ "The color table, color rules and raster map parameters are mutually exclusive. You need to set one and only one of them!"
+ )
return True, None
@@ -43,23 +45,23 @@ def checkParameterValuesBeforeExecuting(alg, parameters, context):
def processInputs(alg, parameters, context, feedback):
# import all rasters with their color tables (and their bands)
# We need to import all the bands and color tables of the input rasters
- rasters = alg.parameterAsLayerList(parameters, 'map', context)
+ rasters = alg.parameterAsLayerList(parameters, "map", context)
for idx, layer in enumerate(rasters):
- layerName = 'map_{}'.format(idx)
+ layerName = f"map_{idx}"
# Add a raster layer
alg.loadRasterLayer(layerName, layer, context, False, None)
# Optional raster layer to copy from
- raster = alg.parameterAsString(parameters, 'raster', context)
+ raster = alg.parameterAsString(parameters, "raster", context)
if raster:
- alg.loadRasterLayerFromParameter('raster', parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("raster", parameters, context, False, None)
alg.postInputs(context)
def processCommand(alg, parameters, context, feedback):
# Handle inline rules
- txtRules = alg.parameterAsString(parameters, 'txtrules', context)
+ txtRules = alg.parameterAsString(parameters, "txtrules", context)
if txtRules:
# Creates a temporary txt file
tempRulesName = getTempFilename(context=context)
@@ -67,11 +69,11 @@ def processCommand(alg, parameters, context, feedback):
# Inject rules into temporary txt file
with open(tempRulesName, "w") as tempRules:
tempRules.write(txtRules)
- alg.removeParameter('txtrules')
- parameters['rules'] = tempRulesName
+ alg.removeParameter("txtrules")
+ parameters["rules"] = tempRulesName
- if alg.parameterAsEnum(parameters, 'color', context) == 0:
- alg.removeParameter('color')
+ if alg.parameterAsEnum(parameters, "color", context) == 0:
+ alg.removeParameter("color")
alg.processCommand(parameters, context, feedback, True)
@@ -81,11 +83,17 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# Export all rasters with their color tables (and their bands)
- rasters = alg.parameterAsLayerList(parameters, 'map', context)
- outputDir = alg.parameterAsString(parameters, 'output_dir', context)
+ rasters = alg.parameterAsLayerList(parameters, "map", context)
+ outputDir = alg.parameterAsString(parameters, "output_dir", context)
for idx, raster in enumerate(rasters):
- rasterName = 'map_{}'.format(idx)
+ rasterName = f"map_{idx}"
fileName = os.path.join(outputDir, rasterName)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- alg.exportRasterLayer(alg.exportedLayers[rasterName], fileName, True,
- outFormat, createOpt, metaOpt)
+ alg.exportRasterLayer(
+ alg.exportedLayers[rasterName],
+ fileName,
+ True,
+ outFormat,
+ createOpt,
+ metaOpt,
+ )
diff --git a/python/plugins/grassprovider/ext/r_colors_stddev.py b/python/plugins/grassprovider/ext/r_colors_stddev.py
index fd3602af5397..e65e8f36e807 100644
--- a/python/plugins/grassprovider/ext/r_colors_stddev.py
+++ b/python/plugins/grassprovider/ext/r_colors_stddev.py
@@ -15,20 +15,20 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from grassprovider.grass_utils import GrassUtils
def processInputs(alg, parameters, context, feedback):
# We need to import all the bands and to preserve color table
- if 'map' in alg.exportedLayers:
+ if "map" in alg.exportedLayers:
return
# We need to import all the bands and color tables of the input raster
- alg.loadRasterLayerFromParameter('map', parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("map", parameters, context, False, None)
alg.postInputs(context)
@@ -42,8 +42,7 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# We need to export the raster with all its bands and its color table
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- grassName = alg.exportedLayers['map']
- alg.exportRasterLayer(grassName, fileName, True,
- outFormat, createOpt, metaOpt)
+ grassName = alg.exportedLayers["map"]
+ alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_drain.py b/python/plugins/grassprovider/ext/r_drain.py
index 383ce86b82f5..6a2ee0b65636 100644
--- a/python/plugins/grassprovider/ext/r_drain.py
+++ b/python/plugins/grassprovider/ext/r_drain.py
@@ -15,25 +15,32 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
+ """Verify if we have the right parameters"""
# start_coordinates and start_points are mutually exclusives
- if (alg.parameterAsString(parameters, 'start_coordinates', context)
- and alg.parameterAsVectorLayer(parameters, 'start_points', context)):
- return False, alg.tr("You need to set either start coordinates OR a start points vector layer!")
+ if alg.parameterAsString(
+ parameters, "start_coordinates", context
+ ) and alg.parameterAsVectorLayer(parameters, "start_points", context):
+ return False, alg.tr(
+ "You need to set either start coordinates OR a start points vector layer!"
+ )
# You need to set at least one parameter
- if (not alg.parameterAsString(parameters, 'start_coordinates', context)
- and not alg.parameterAsVectorLayer(parameters, 'start_points', context)):
- return False, alg.tr("You need to set either start coordinates OR a start points vector layer!")
+ if not alg.parameterAsString(
+ parameters, "start_coordinates", context
+ ) and not alg.parameterAsVectorLayer(parameters, "start_points", context):
+ return False, alg.tr(
+ "You need to set either start coordinates OR a start points vector layer!"
+ )
- paramscore = [f for f in ['-c', '-a', '-n']
- if alg.parameterAsBoolean(parameters, f, context)]
+ paramscore = [
+ f for f in ["-c", "-a", "-n"] if alg.parameterAsBoolean(parameters, f, context)
+ ]
if len(paramscore) > 1:
return False, alg.tr("-c, -a, -n parameters are mutually exclusive!")
return True, None
diff --git a/python/plugins/grassprovider/ext/r_horizon.py b/python/plugins/grassprovider/ext/r_horizon.py
index b287352c8c82..36a4b024d646 100644
--- a/python/plugins/grassprovider/ext/r_horizon.py
+++ b/python/plugins/grassprovider/ext/r_horizon.py
@@ -15,19 +15,19 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'September 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "September 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
import os
import math
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- start = alg.parameterAsDouble(parameters, 'start', context)
- end = alg.parameterAsDouble(parameters, 'end', context)
- step = alg.parameterAsDouble(parameters, 'step', context)
+ """Verify if we have the right parameters"""
+ start = alg.parameterAsDouble(parameters, "start", context)
+ end = alg.parameterAsDouble(parameters, "end", context)
+ step = alg.parameterAsDouble(parameters, "step", context)
if start >= end:
return False, alg.tr("The start position must be inferior to the end position!")
@@ -39,7 +39,7 @@ def checkParameterValuesBeforeExecuting(alg, parameters, context):
def processOutputs(alg, parameters, context, feedback):
# Inspired from GRASS implementation
def getNumberDecimals(number):
- """ Return the number of decimals of a number (int or float) """
+ """Return the number of decimals of a number (int or float)"""
if int(number) == number:
return 0
return len(str(number).split(".")[-1])
@@ -52,28 +52,28 @@ def doubleToBaseName(number, nDecimals):
number += 0.0001
# adapted from GRASS https://github.com/OSGeo/grass/blob/6253da1bd6ce48d23419e99e8b503edf46178490/lib/gis/basename.c#L97-L101
if nDecimals == 0:
- return f'{int(number):03}'
+ return f"{int(number):03}"
int_part = int(number)
dec_part = int((number - int_part) * pow(10, nDecimals))
return f'{int_part:03}_{str(dec_part).rjust(nDecimals, "0")}'
# There will be as many outputs as the difference between start and end divided by steps
- start = alg.parameterAsDouble(parameters, 'start', context)
- end = alg.parameterAsDouble(parameters, 'end', context)
- step = alg.parameterAsDouble(parameters, 'step', context)
- direction = alg.parameterAsDouble(parameters, 'direction', context)
+ start = alg.parameterAsDouble(parameters, "start", context)
+ end = alg.parameterAsDouble(parameters, "end", context)
+ step = alg.parameterAsDouble(parameters, "step", context)
+ direction = alg.parameterAsDouble(parameters, "direction", context)
first_rad = math.radians(start + direction)
nDecimals = getNumberDecimals(step)
dfr_rad = math.radians(step)
arrayNumInt = int((end - start) / abs(step))
- directory = alg.parameterAsString(parameters, 'output', context)
+ directory = alg.parameterAsString(parameters, "output", context)
# Needed if output to a temporary directory
os.makedirs(directory, exist_ok=True)
for k in range(arrayNumInt):
angle_deg = math.degrees(first_rad + dfr_rad * k)
baseName = doubleToBaseName(angle_deg, nDecimals)
- grassName = f'output{alg.uniqueSuffix}_{baseName}'
- fileName = f'{os.path.join(directory, baseName)}.tif'
+ grassName = f"output{alg.uniqueSuffix}_{baseName}"
+ fileName = f"{os.path.join(directory, baseName)}.tif"
alg.exportRasterLayer(grassName, fileName)
diff --git a/python/plugins/grassprovider/ext/r_li.py b/python/plugins/grassprovider/ext/r_li.py
index 351414bc7c5d..4d8fad728606 100644
--- a/python/plugins/grassprovider/ext/r_li.py
+++ b/python/plugins/grassprovider/ext/r_li.py
@@ -15,68 +15,75 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import shutil
from qgis.core import QgsProcessingParameterString
-from processing.tools.system import (isWindows, mkdir,
- getTempFilename)
+from processing.tools.system import isWindows, mkdir, getTempFilename
from grassprovider.grass_utils import GrassUtils
import os
# for MS-Windows users who have MBCS chars in their name:
-if os.name == 'nt':
+if os.name == "nt":
import win32api
def rliPath():
"""Return r.li GRASS user dir"""
- grass_version = GrassUtils.installedVersion().split('.')[0]
+ grass_version = GrassUtils.installedVersion().split(".")[0]
if isWindows():
- homeDir = win32api.GetShortPathName(os.path.expanduser('~'))
- return os.path.join(homeDir, 'AppData', 'Roaming', f'GRASS{grass_version}', 'r.li')
+ homeDir = win32api.GetShortPathName(os.path.expanduser("~"))
+ return os.path.join(
+ homeDir, "AppData", "Roaming", f"GRASS{grass_version}", "r.li"
+ )
else:
- return os.path.join(os.path.expanduser("~"), f'.grass{grass_version}', 'r.li')
+ return os.path.join(os.path.expanduser("~"), f".grass{grass_version}", "r.li")
def removeConfigFile(alg, parameters, context):
- """ Remove the r.li user dir config file """
- configPath = alg.parameterAsString(parameters, 'config', context)
+ """Remove the r.li user dir config file"""
+ configPath = alg.parameterAsString(parameters, "config", context)
if isWindows():
- command = "DEL {}".format(os.path.join(rliPath(), configPath))
+ command = f"DEL {os.path.join(rliPath(), configPath)}"
else:
- command = "rm {}".format(os.path.join(rliPath(), configPath))
+ command = f"rm {os.path.join(rliPath(), configPath)}"
alg.commands.append(command)
def checkMovingWindow(alg, parameters, context, outputTxt=False):
- """ Verify if we have the right parameters """
- configTxt = alg.parameterAsString(parameters, 'config_txt', context)
- config = alg.parameterAsString(parameters, 'config', context)
+ """Verify if we have the right parameters"""
+ configTxt = alg.parameterAsString(parameters, "config_txt", context)
+ config = alg.parameterAsString(parameters, "config", context)
if configTxt and config:
- return False, alg.tr("You need to set either inline configuration or a configuration file!")
+ return False, alg.tr(
+ "You need to set either inline configuration or a configuration file!"
+ )
# Verify that configuration is in moving window
movingWindow = False
if configTxt:
- if 'MOVINGWINDOW' in configTxt:
+ if "MOVINGWINDOW" in configTxt:
movingWindow = True
# Read config file:
if config:
with open(config) as f:
for line in f:
- if 'MOVINGWINDOW' in line:
+ if "MOVINGWINDOW" in line:
movingWindow = True
if not movingWindow and not outputTxt:
- return False, alg.tr('Your configuration needs to be a "moving window" configuration!')
+ return False, alg.tr(
+ 'Your configuration needs to be a "moving window" configuration!'
+ )
if movingWindow and outputTxt:
- return False, alg.tr('Your configuration needs to be a non "moving window" configuration!')
+ return False, alg.tr(
+ 'Your configuration needs to be a non "moving window" configuration!'
+ )
return True, None
@@ -89,37 +96,40 @@ def configFile(alg, parameters, context, feedback, outputTxt=False):
user_grass_path = rliPath()
if not os.path.isdir(user_grass_path):
mkdir(user_grass_path)
- if not os.path.isdir(os.path.join(user_grass_path, 'output')):
- mkdir(os.path.join(user_grass_path, 'output'))
+ if not os.path.isdir(os.path.join(user_grass_path, "output")):
+ mkdir(os.path.join(user_grass_path, "output"))
# If we have a configuration file, we need to copy it into user dir
- if parameters['config']:
- fileName = alg.parameterAsString(parameters, 'config', context)
+ if parameters["config"]:
+ fileName = alg.parameterAsString(parameters, "config", context)
configFilePath = os.path.join(user_grass_path, os.path.basename(fileName))
# Copy the file
- shutil.copy(parameters['config'], configFilePath)
+ shutil.copy(parameters["config"], configFilePath)
# Change the parameter value
- parameters['config'] = os.path.basename(configFilePath)
+ parameters["config"] = os.path.basename(configFilePath)
# Handle inline configuration
- elif parameters['config_txt']:
+ elif parameters["config_txt"]:
# Creates a temporary txt file in user r.li directory
tempConfig = os.path.basename(getTempFilename(context=context))
configFilePath = os.path.join(user_grass_path, tempConfig)
# Inject rules into temporary txt file
with open(configFilePath, "w") as f:
- f.write(alg.parameterAsString(parameters, 'config_txt', context))
+ f.write(alg.parameterAsString(parameters, "config_txt", context))
f.write("\n")
# Use temporary file as rules file
- parameters['config'] = os.path.basename(configFilePath)
- alg.removeParameter('config_txt')
+ parameters["config"] = os.path.basename(configFilePath)
+ alg.removeParameter("config_txt")
# For ascii output, we need a virtual output
if outputTxt:
param = QgsProcessingParameterString(
- 'output', 'virtual output',
- 'a' + os.path.basename(getTempFilename(context=context)),
- False, False)
+ "output",
+ "virtual output",
+ "a" + os.path.basename(getTempFilename(context=context)),
+ False,
+ False,
+ )
alg.addParameter(param)
alg.processCommand(parameters, context, feedback, outputTxt)
@@ -130,14 +140,15 @@ def configFile(alg, parameters, context, feedback, outputTxt=False):
def moveOutputTxtFile(alg, parameters, context):
# Find output file name:
- txtPath = alg.parameterAsString(parameters, 'output_txt', context)
+ txtPath = alg.parameterAsString(parameters, "output_txt", context)
user_grass_path = rliPath()
- output = os.path.join(user_grass_path, 'output',
- alg.parameterAsString(parameters, 'output', context))
+ output = os.path.join(
+ user_grass_path, "output", alg.parameterAsString(parameters, "output", context)
+ )
# move the file
if isWindows():
- command = "MOVE /Y {} {}".format(output, txtPath)
+ command = f"MOVE /Y {output} {txtPath}"
else:
- command = "mv -f {} {}".format(output, txtPath)
+ command = f"mv -f {output} {txtPath}"
alg.commands.append(command)
diff --git a/python/plugins/grassprovider/ext/r_li_cwed.py b/python/plugins/grassprovider/ext/r_li_cwed.py
index 50e80c989b81..185746668b3d 100644
--- a/python/plugins/grassprovider/ext/r_li_cwed.py
+++ b/python/plugins/grassprovider/ext/r_li_cwed.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_cwed_ascii.py b/python/plugins/grassprovider/ext/r_li_cwed_ascii.py
index 497a882d4245..8052ce48e699 100644
--- a/python/plugins/grassprovider/ext/r_li_cwed_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_cwed_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_dominance.py b/python/plugins/grassprovider/ext/r_li_dominance.py
index 6e3a0a3eefd9..c869f3efc300 100644
--- a/python/plugins/grassprovider/ext/r_li_dominance.py
+++ b/python/plugins/grassprovider/ext/r_li_dominance.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_dominance_ascii.py b/python/plugins/grassprovider/ext/r_li_dominance_ascii.py
index eb6b77f96340..a8aed9fa2ca2 100644
--- a/python/plugins/grassprovider/ext/r_li_dominance_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_dominance_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_edgedensity.py b/python/plugins/grassprovider/ext/r_li_edgedensity.py
index 5beac0885069..165c6f4030ca 100644
--- a/python/plugins/grassprovider/ext/r_li_edgedensity.py
+++ b/python/plugins/grassprovider/ext/r_li_edgedensity.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_edgedensity_ascii.py b/python/plugins/grassprovider/ext/r_li_edgedensity_ascii.py
index a416d6ed68d4..2c4c4ed3c48f 100644
--- a/python/plugins/grassprovider/ext/r_li_edgedensity_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_edgedensity_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_mpa.py b/python/plugins/grassprovider/ext/r_li_mpa.py
index 1fd13334eccd..e2a481a8cc06 100644
--- a/python/plugins/grassprovider/ext/r_li_mpa.py
+++ b/python/plugins/grassprovider/ext/r_li_mpa.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_mpa_ascii.py b/python/plugins/grassprovider/ext/r_li_mpa_ascii.py
index 90597643824f..22504a71c849 100644
--- a/python/plugins/grassprovider/ext/r_li_mpa_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_mpa_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_mps.py b/python/plugins/grassprovider/ext/r_li_mps.py
index f7de0f61977d..cdeed825ea57 100644
--- a/python/plugins/grassprovider/ext/r_li_mps.py
+++ b/python/plugins/grassprovider/ext/r_li_mps.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_mps_ascii.py b/python/plugins/grassprovider/ext/r_li_mps_ascii.py
index eac3c41b64b8..b36396ac788b 100644
--- a/python/plugins/grassprovider/ext/r_li_mps_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_mps_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_padcv.py b/python/plugins/grassprovider/ext/r_li_padcv.py
index cfb4310e8b5c..1f90af63f38d 100644
--- a/python/plugins/grassprovider/ext/r_li_padcv.py
+++ b/python/plugins/grassprovider/ext/r_li_padcv.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_padcv_ascii.py b/python/plugins/grassprovider/ext/r_li_padcv_ascii.py
index 0e045ec1471d..63d2cca7151f 100644
--- a/python/plugins/grassprovider/ext/r_li_padcv_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_padcv_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_padrange.py b/python/plugins/grassprovider/ext/r_li_padrange.py
index 24db8ae167d1..f520a0de8b97 100644
--- a/python/plugins/grassprovider/ext/r_li_padrange.py
+++ b/python/plugins/grassprovider/ext/r_li_padrange.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_padrange_ascii.py b/python/plugins/grassprovider/ext/r_li_padrange_ascii.py
index 0d7d61f1a87c..57856b40ae49 100644
--- a/python/plugins/grassprovider/ext/r_li_padrange_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_padrange_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_padsd.py b/python/plugins/grassprovider/ext/r_li_padsd.py
index 17b29fc05885..f6bce09eb436 100644
--- a/python/plugins/grassprovider/ext/r_li_padsd.py
+++ b/python/plugins/grassprovider/ext/r_li_padsd.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_padsd_ascii.py b/python/plugins/grassprovider/ext/r_li_padsd_ascii.py
index 8c7e7c22c5ee..c5a700daa7d1 100644
--- a/python/plugins/grassprovider/ext/r_li_padsd_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_padsd_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_patchdensity.py b/python/plugins/grassprovider/ext/r_li_patchdensity.py
index acc305aa828d..d658f4a3145c 100644
--- a/python/plugins/grassprovider/ext/r_li_patchdensity.py
+++ b/python/plugins/grassprovider/ext/r_li_patchdensity.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_patchdensity_ascii.py b/python/plugins/grassprovider/ext/r_li_patchdensity_ascii.py
index 111efcbb18d7..620c33db1649 100644
--- a/python/plugins/grassprovider/ext/r_li_patchdensity_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_patchdensity_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_patchnum.py b/python/plugins/grassprovider/ext/r_li_patchnum.py
index 90f4ad460913..925b50620102 100644
--- a/python/plugins/grassprovider/ext/r_li_patchnum.py
+++ b/python/plugins/grassprovider/ext/r_li_patchnum.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_patchnum_ascii.py b/python/plugins/grassprovider/ext/r_li_patchnum_ascii.py
index ec2da01d8cf1..0ba1cd84e424 100644
--- a/python/plugins/grassprovider/ext/r_li_patchnum_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_patchnum_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_pielou.py b/python/plugins/grassprovider/ext/r_li_pielou.py
index f1a636527ad4..52af65e5efc4 100644
--- a/python/plugins/grassprovider/ext/r_li_pielou.py
+++ b/python/plugins/grassprovider/ext/r_li_pielou.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_pielou_ascii.py b/python/plugins/grassprovider/ext/r_li_pielou_ascii.py
index 4ce58b91c42a..87162f84723e 100644
--- a/python/plugins/grassprovider/ext/r_li_pielou_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_pielou_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_renyi.py b/python/plugins/grassprovider/ext/r_li_renyi.py
index 5e34cad1df1c..8bacea15cf8c 100644
--- a/python/plugins/grassprovider/ext/r_li_renyi.py
+++ b/python/plugins/grassprovider/ext/r_li_renyi.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_renyi_ascii.py b/python/plugins/grassprovider/ext/r_li_renyi_ascii.py
index 0d9925d9326d..2daea6fd3251 100644
--- a/python/plugins/grassprovider/ext/r_li_renyi_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_renyi_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_richness.py b/python/plugins/grassprovider/ext/r_li_richness.py
index fcfdcb2ad357..dc1c3c00908f 100644
--- a/python/plugins/grassprovider/ext/r_li_richness.py
+++ b/python/plugins/grassprovider/ext/r_li_richness.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_richness_ascii.py b/python/plugins/grassprovider/ext/r_li_richness_ascii.py
index 2061d7a31be8..0eb2e6f85d76 100644
--- a/python/plugins/grassprovider/ext/r_li_richness_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_richness_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_shannon.py b/python/plugins/grassprovider/ext/r_li_shannon.py
index cb9d29700299..4d1ac79ab3de 100644
--- a/python/plugins/grassprovider/ext/r_li_shannon.py
+++ b/python/plugins/grassprovider/ext/r_li_shannon.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_shannon_ascii.py b/python/plugins/grassprovider/ext/r_li_shannon_ascii.py
index 5614f5b92390..694cd1b3acaa 100644
--- a/python/plugins/grassprovider/ext/r_li_shannon_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_shannon_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_shape.py b/python/plugins/grassprovider/ext/r_li_shape.py
index 2c2a47a6e7c7..5f389425145e 100644
--- a/python/plugins/grassprovider/ext/r_li_shape.py
+++ b/python/plugins/grassprovider/ext/r_li_shape.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_shape_ascii.py b/python/plugins/grassprovider/ext/r_li_shape_ascii.py
index fc6a99a00005..681426c94186 100644
--- a/python/plugins/grassprovider/ext/r_li_shape_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_shape_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_li_simpson.py b/python/plugins/grassprovider/ext/r_li_simpson.py
index b6486085d4df..6d54abfd0d2d 100644
--- a/python/plugins/grassprovider/ext/r_li_simpson.py
+++ b/python/plugins/grassprovider/ext/r_li_simpson.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile
diff --git a/python/plugins/grassprovider/ext/r_li_simpson_ascii.py b/python/plugins/grassprovider/ext/r_li_simpson_ascii.py
index 8baf6755b15c..9d92f8c46a6c 100644
--- a/python/plugins/grassprovider/ext/r_li_simpson_ascii.py
+++ b/python/plugins/grassprovider/ext/r_li_simpson_ascii.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from .r_li import checkMovingWindow, configFile, moveOutputTxtFile
diff --git a/python/plugins/grassprovider/ext/r_mask_rast.py b/python/plugins/grassprovider/ext/r_mask_rast.py
index 6d36a2eede9e..6adc80915266 100644
--- a/python/plugins/grassprovider/ext/r_mask_rast.py
+++ b/python/plugins/grassprovider/ext/r_mask_rast.py
@@ -15,16 +15,16 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from grassprovider.grass_utils import GrassUtils
def processCommand(alg, parameters, context, feedback):
# Remove input
- alg.removeParameter('input')
+ alg.removeParameter("input")
alg.processCommand(parameters, context, feedback, True)
@@ -33,8 +33,7 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# We need to export the raster with all its bands and its color table
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- grassName = alg.exportedLayers['input']
- alg.exportRasterLayer(grassName, fileName, True,
- outFormat, createOpt, metaOpt)
+ grassName = alg.exportedLayers["input"]
+ alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_mask_vect.py b/python/plugins/grassprovider/ext/r_mask_vect.py
index 40e5fb2c3f73..f0a768fcb3b9 100644
--- a/python/plugins/grassprovider/ext/r_mask_vect.py
+++ b/python/plugins/grassprovider/ext/r_mask_vect.py
@@ -15,16 +15,16 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from grassprovider.grass_utils import GrassUtils
def processCommand(alg, parameters, context, feedback):
# Remove input
- alg.removeParameter('input')
+ alg.removeParameter("input")
alg.processCommand(parameters, context, feedback, True)
@@ -33,8 +33,7 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# We need to export the raster with all its bands and its color table
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- grassName = alg.exportedLayers['input']
- alg.exportRasterLayer(grassName, fileName, True,
- outFormat, createOpt, metaOpt)
+ grassName = alg.exportedLayers["input"]
+ alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_neighbors.py b/python/plugins/grassprovider/ext/r_neighbors.py
index 256b0313912c..d01f3c45a2dc 100644
--- a/python/plugins/grassprovider/ext/r_neighbors.py
+++ b/python/plugins/grassprovider/ext/r_neighbors.py
@@ -15,15 +15,15 @@
***************************************************************************
"""
-__author__ = 'Nicolas Godet'
-__date__ = 'June 2021'
-__copyright__ = '(C) 2021, Nicolas Godet'
+__author__ = "Nicolas Godet"
+__date__ = "June 2021"
+__copyright__ = "(C) 2021, Nicolas Godet"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
+ """Verify if we have the right parameters"""
# Verifiy that neighborhood size is odd
- if (alg.parameterAsInt(parameters, 'size', context) % 2) == 0:
+ if (alg.parameterAsInt(parameters, "size", context) % 2) == 0:
return False, alg.tr("The neighborhood size must be odd!")
return True, None
diff --git a/python/plugins/grassprovider/ext/r_null.py b/python/plugins/grassprovider/ext/r_null.py
index a3a1d0e82e71..1df7db79d716 100644
--- a/python/plugins/grassprovider/ext/r_null.py
+++ b/python/plugins/grassprovider/ext/r_null.py
@@ -15,27 +15,30 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- if (alg.parameterAsString(parameters, 'setnull', context)
- or alg.parameterAsString(parameters, 'null', context)):
+ """Verify if we have the right parameters"""
+ if alg.parameterAsString(parameters, "setnull", context) or alg.parameterAsString(
+ parameters, "null", context
+ ):
return True, None
- return False, alg.tr("You need to set at least 'setnull' or 'null' parameters for this algorithm!")
+ return False, alg.tr(
+ "You need to set at least 'setnull' or 'null' parameters for this algorithm!"
+ )
def processInputs(alg, parameters, context, feedback):
"""Prepare the GRASS import commands"""
- if 'map' in alg.exportedLayers:
+ if "map" in alg.exportedLayers:
return
# We need to import without r.external
- alg.loadRasterLayerFromParameter('map', parameters, context, False)
+ alg.loadRasterLayerFromParameter("map", parameters, context, False)
alg.postInputs(context)
@@ -45,6 +48,6 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['map']
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["map"]
alg.exportRasterLayer(grassName, fileName, False)
diff --git a/python/plugins/grassprovider/ext/r_proj.py b/python/plugins/grassprovider/ext/r_proj.py
index 46d0f7549308..5899948ed329 100644
--- a/python/plugins/grassprovider/ext/r_proj.py
+++ b/python/plugins/grassprovider/ext/r_proj.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'October 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "October 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from qgis.core import QgsProcessingParameterString
from processing.tools.system import isWindows
@@ -26,50 +26,51 @@
def processInputs(alg, parameters, context, feedback):
# Grab the projection from the input vector layer
- layer = alg.parameterAsLayer(parameters, 'input', context)
+ layer = alg.parameterAsLayer(parameters, "input", context)
# Creates a new location with this Crs
wkt_file_name = GrassUtils.exportCrsWktToFile(layer.crs(), context)
- newLocation = 'newProj{}'.format(alg.uniqueSuffix)
- alg.commands.append('g.proj wkt="{}" location={}'.format(
- wkt_file_name, newLocation))
+ newLocation = f"newProj{alg.uniqueSuffix}"
+ alg.commands.append(f'g.proj wkt="{wkt_file_name}" location={newLocation}')
# Go to the newly created location
- alg.commands.append('g.mapset mapset=PERMANENT location={}'.format(
- newLocation))
+ alg.commands.append(f"g.mapset mapset=PERMANENT location={newLocation}")
# Import the layer
- alg.loadRasterLayerFromParameter(
- 'input', parameters, context, False)
+ alg.loadRasterLayerFromParameter("input", parameters, context, False)
# Go back to default location
- alg.commands.append('g.mapset mapset=PERMANENT location=temp_location')
+ alg.commands.append("g.mapset mapset=PERMANENT location=temp_location")
# Grab the projected Crs
- crs = alg.parameterAsCrs(parameters, 'crs', context)
+ crs = alg.parameterAsCrs(parameters, "crs", context)
wkt_file_name = GrassUtils.exportCrsWktToFile(crs, context)
- alg.commands.append('g.proj -c wkt="{}"'.format(wkt_file_name))
+ alg.commands.append(f'g.proj -c wkt="{wkt_file_name}"')
# Remove crs parameter
- alg.removeParameter('crs')
+ alg.removeParameter("crs")
# Add the location parameter with proper value
location = QgsProcessingParameterString(
- 'location',
- 'new location',
- 'newProj{}'.format(alg.uniqueSuffix)
+ "location", "new location", f"newProj{alg.uniqueSuffix}"
)
alg.addParameter(location)
# And set the region
- grassName = alg.exportedLayers['input']
+ grassName = alg.exportedLayers["input"]
# We use the shell to capture the results from r.proj -g
if isWindows():
# TODO: make some tests under a non POSIX shell
- alg.commands.append('set regVar=')
- alg.commands.append('for /f "delims=" %%a in (\'r.proj -g input^="{}" location^="{}"\') do @set regVar=%%a'.format(
- grassName, newLocation))
- alg.commands.append('g.region -a %regVar%')
+ alg.commands.append("set regVar=")
+ alg.commands.append(
+ 'for /f "delims=" %%a in (\'r.proj -g input^="{}" location^="{}"\') do @set regVar=%%a'.format(
+ grassName, newLocation
+ )
+ )
+ alg.commands.append("g.region -a %regVar%")
else:
- alg.commands.append('g.region -a $(r.proj -g input="{}" location="{}")'.format(
- grassName, newLocation))
+ alg.commands.append(
+ 'g.region -a $(r.proj -g input="{}" location="{}")'.format(
+ grassName, newLocation
+ )
+ )
diff --git a/python/plugins/grassprovider/ext/r_reclass.py b/python/plugins/grassprovider/ext/r_reclass.py
index f29bf528b127..13cfb74f5793 100644
--- a/python/plugins/grassprovider/ext/r_reclass.py
+++ b/python/plugins/grassprovider/ext/r_reclass.py
@@ -15,25 +15,28 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from processing.tools.system import getTempFilename
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- if (alg.parameterAsString(parameters, 'rules', context)
- and alg.parameterAsString(parameters, 'txtrules', context)):
- return False, alg.tr("You need to set either a rules file or write directly the rules!")
+ """Verify if we have the right parameters"""
+ if alg.parameterAsString(parameters, "rules", context) and alg.parameterAsString(
+ parameters, "txtrules", context
+ ):
+ return False, alg.tr(
+ "You need to set either a rules file or write directly the rules!"
+ )
return True, None
def processCommand(alg, parameters, context, feedback):
- """ Handle inline rules """
- txtRules = alg.parameterAsString(parameters, 'txtrules', context)
+ """Handle inline rules"""
+ txtRules = alg.parameterAsString(parameters, "txtrules", context)
if txtRules:
# Creates a temporary txt file
tempRulesName = getTempFilename(context=context)
@@ -41,7 +44,7 @@ def processCommand(alg, parameters, context, feedback):
# Inject rules into temporary txt file
with open(tempRulesName, "w") as tempRules:
tempRules.write(txtRules)
- alg.removeParameter('txtrules')
- parameters['rules'] = tempRulesName
+ alg.removeParameter("txtrules")
+ parameters["rules"] = tempRulesName
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/r_resamp_filter.py b/python/plugins/grassprovider/ext/r_resamp_filter.py
index df57b247b771..7b0453c9bb38 100644
--- a/python/plugins/grassprovider/ext/r_resamp_filter.py
+++ b/python/plugins/grassprovider/ext/r_resamp_filter.py
@@ -15,18 +15,20 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- radius = alg.parameterAsString(parameters, 'radius', context)
- x_radius = alg.parameterAsString(parameters, 'x_radius', context)
- y_radius = alg.parameterAsString(parameters, 'y_radius', context)
+ """Verify if we have the right parameters"""
+ radius = alg.parameterAsString(parameters, "radius", context)
+ x_radius = alg.parameterAsString(parameters, "x_radius", context)
+ y_radius = alg.parameterAsString(parameters, "y_radius", context)
- if (not radius and not x_radius and not y_radius) or (radius and (x_radius or y_radius)):
+ if (not radius and not x_radius and not y_radius) or (
+ radius and (x_radius or y_radius)
+ ):
return False, alg.tr("You need to set either radius or x_radius and y_radius!")
elif (x_radius and not y_radius) or (y_radius and not x_radius):
return False, alg.tr("You need to set x_radius and y_radius!")
diff --git a/python/plugins/grassprovider/ext/r_rgb.py b/python/plugins/grassprovider/ext/r_rgb.py
index bc8d3e7b3daf..257912a7539a 100644
--- a/python/plugins/grassprovider/ext/r_rgb.py
+++ b/python/plugins/grassprovider/ext/r_rgb.py
@@ -15,45 +15,47 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
- if 'input' in alg.exportedLayers:
+ if "input" in alg.exportedLayers:
return
# We need to import all the bands and color tables of the input raster
- alg.loadRasterLayerFromParameter('input', parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("input", parameters, context, False, None)
alg.postInputs(context)
def processCommand(alg, parameters, context, feedback):
# if the input raster is multiband: export each component directly
- rasterInput = alg.exportedLayers['input']
- raster = alg.parameterAsRasterLayer(parameters, 'input', context)
- for color in ['red', 'green', 'blue']:
+ rasterInput = alg.exportedLayers["input"]
+ raster = alg.parameterAsRasterLayer(parameters, "input", context)
+ for color in ["red", "green", "blue"]:
alg.exportedLayers[color] = color + alg.uniqueSuffix
# If the raster is not multiband, really do r.rgb
if raster.bandCount() == 1:
- alg.commands.append(" r.rgb input={} red={} green={} blue={} --overwrite".format(
- rasterInput,
- alg.exportedLayers['red'],
- alg.exportedLayers['green'],
- alg.exportedLayers['blue']
- ))
+ alg.commands.append(
+ " r.rgb input={} red={} green={} blue={} --overwrite".format(
+ rasterInput,
+ alg.exportedLayers["red"],
+ alg.exportedLayers["green"],
+ alg.exportedLayers["blue"],
+ )
+ )
def processOutputs(alg, parameters, context, feedback):
- raster = alg.parameterAsRasterLayer(parameters, 'input', context)
+ raster = alg.parameterAsRasterLayer(parameters, "input", context)
# if the raster was monoband, export from r.rgb
- for color in ['red', 'green', 'blue']:
+ for color in ["red", "green", "blue"]:
fileName = alg.parameterAsOutputLayer(parameters, color, context)
if raster.bandCount() == 1:
- grassName = '{}{}'.format(color, alg.uniqueSuffix)
+ grassName = f"{color}{alg.uniqueSuffix}"
else:
- grassName = '{}.{}'.format(alg.exportedLayers['input'], color)
+ grassName = "{}.{}".format(alg.exportedLayers["input"], color)
alg.exportRasterLayer(grassName, fileName, True)
diff --git a/python/plugins/grassprovider/ext/r_series_interp.py b/python/plugins/grassprovider/ext/r_series_interp.py
index 0135c46d3f5b..4a4cd6462d50 100644
--- a/python/plugins/grassprovider/ext/r_series_interp.py
+++ b/python/plugins/grassprovider/ext/r_series_interp.py
@@ -15,27 +15,33 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from grassprovider.grass_utils import GrassUtils
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- datapos = alg.parameterAsDouble(parameters, 'datapos', context)
- infile = alg.parameterAsString(parameters, 'infile', context)
- output = alg.parameterAsString(parameters, 'output', context)
- outfile = alg.parameterAsString(parameters, 'outfile', context)
+ """Verify if we have the right parameters"""
+ datapos = alg.parameterAsDouble(parameters, "datapos", context)
+ infile = alg.parameterAsString(parameters, "infile", context)
+ output = alg.parameterAsString(parameters, "output", context)
+ outfile = alg.parameterAsString(parameters, "outfile", context)
if datapos and infile:
- return False, alg.tr("You need to set either inline data positions or an input data positions file!")
+ return False, alg.tr(
+ "You need to set either inline data positions or an input data positions file!"
+ )
if output and outfile:
- return False, alg.tr("You need to set either sampling data positions or an output sampling data positions file!")
+ return False, alg.tr(
+ "You need to set either sampling data positions or an output sampling data positions file!"
+ )
if not (datapos or infile or output or outfile):
- return False, alg.tr("You need to set input and output data positions parameters!")
+ return False, alg.tr(
+ "You need to set input and output data positions parameters!"
+ )
return True, None
@@ -46,18 +52,18 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# We take all the outputs and we export them to the output directory
- outputDir = alg.parameterAsString(parameters, 'output_dir', context)
- output = alg.parameterAsString(parameters, 'output', context)
- outfile = alg.parameterAsString(parameters, 'outfile', context)
+ outputDir = alg.parameterAsString(parameters, "output_dir", context)
+ output = alg.parameterAsString(parameters, "output", context)
+ outfile = alg.parameterAsString(parameters, "outfile", context)
outs = []
if output:
- outs = output.split(',')
+ outs = output.split(",")
elif outfile:
# Handle file manually to find the name of the layers
with open(outfile) as f:
for line in f:
- if '|' in line:
- outs.append(line.split('|')[0])
+ if "|" in line:
+ outs.append(line.split("|")[0])
createOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_OPT, context)
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
@@ -66,5 +72,4 @@ def processOutputs(alg, parameters, context, feedback):
# We need to export the raster with all its bands and its color table
fileName = os.path.join(outputDir, out)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- alg.exportRasterLayer(out, fileName, True,
- outFormat, createOpt, metaOpt)
+ alg.exportRasterLayer(out, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_shade.py b/python/plugins/grassprovider/ext/r_shade.py
index bcd6d7aa5a20..2e5588a56733 100644
--- a/python/plugins/grassprovider/ext/r_shade.py
+++ b/python/plugins/grassprovider/ext/r_shade.py
@@ -15,19 +15,17 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
# We need to import all the bands and color tables of the input rasters
- alg.loadRasterLayerFromParameter('shade', parameters, context,
- False, None)
- alg.loadRasterLayerFromParameter('color', parameters, context,
- False, None)
+ alg.loadRasterLayerFromParameter("shade", parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("color", parameters, context, False, None)
def processOutputs(alg, parameters, context, feedback):
# Keep color table
- alg.exportRasterLayerFromParameter('output', parameters, context, True)
+ alg.exportRasterLayerFromParameter("output", parameters, context, True)
diff --git a/python/plugins/grassprovider/ext/r_statistics.py b/python/plugins/grassprovider/ext/r_statistics.py
index 11f403c70782..b822eaa3bc62 100644
--- a/python/plugins/grassprovider/ext/r_statistics.py
+++ b/python/plugins/grassprovider/ext/r_statistics.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'September 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "September 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from qgis.core import QgsProcessingParameterString
from grassprovider.grass_utils import GrassUtils
@@ -25,17 +25,16 @@
def processCommand(alg, parameters, context, feedback):
# We had a new "output" parameter
- out = 'output{}'.format(alg.uniqueSuffix)
- p = QgsProcessingParameterString('~output', None, out, False, False)
+ out = f"output{alg.uniqueSuffix}"
+ p = QgsProcessingParameterString("~output", None, out, False, False)
alg.addParameter(p)
# We need to remove all outputs
alg.processCommand(parameters, context, feedback, True)
# Then we add a new command for treating results
- calcExpression = 'correctedoutput{}=@{}'.format(
- alg.uniqueSuffix, out)
- command = 'r.mapcalc expression="{}"'.format(calcExpression)
+ calcExpression = f"correctedoutput{alg.uniqueSuffix}=@{out}"
+ command = f'r.mapcalc expression="{calcExpression}"'
alg.commands.append(command)
@@ -44,9 +43,7 @@ def processOutputs(alg, parameters, context, feedback):
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
# Export the results from correctedoutput
- grassName = 'correctedoutput{}'.format(alg.uniqueSuffix)
- fileName = alg.parameterAsOutputLayer(
- parameters, 'routput', context)
+ grassName = f"correctedoutput{alg.uniqueSuffix}"
+ fileName = alg.parameterAsOutputLayer(parameters, "routput", context)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- alg.exportRasterLayer(grassName, fileName, True,
- outFormat, createOpt, metaOpt)
+ alg.exportRasterLayer(grassName, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_stats_quantile_rast.py b/python/plugins/grassprovider/ext/r_stats_quantile_rast.py
index ccd8e4ca6316..d66490ba1350 100644
--- a/python/plugins/grassprovider/ext/r_stats_quantile_rast.py
+++ b/python/plugins/grassprovider/ext/r_stats_quantile_rast.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from qgis.core import QgsProcessingParameterString
@@ -26,13 +26,13 @@
def processCommand(alg, parameters, context, feedback):
# We create the output sequence according to percentiles number
- quantiles = alg.parameterAsInt(parameters, 'quantiles', context) - 1
+ quantiles = alg.parameterAsInt(parameters, "quantiles", context) - 1
outputs = []
for i in range(0, int(quantiles)):
- outputs.append('output_{}'.format(i))
+ outputs.append(f"output_{i}")
param = QgsProcessingParameterString(
- 'output', 'virtual output',
- ','.join(outputs), False, False)
+ "output", "virtual output", ",".join(outputs), False, False
+ )
alg.addParameter(param)
# Removes outputs
@@ -42,13 +42,12 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
createOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_OPT, context)
metaOpt = alg.parameterAsString(parameters, alg.GRASS_RASTER_FORMAT_META, context)
- outputDir = alg.parameterAsString(parameters, 'output_dir', context)
- outputParam = alg.parameterAsString(parameters, 'output', context)
- outputs = outputParam.split(',')
+ outputDir = alg.parameterAsString(parameters, "output_dir", context)
+ outputParam = alg.parameterAsString(parameters, "output", context)
+ outputs = outputParam.split(",")
# We need to export each of the output
for output in outputs:
fileName = os.path.join(outputDir, output)
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- alg.exportRasterLayer(output, fileName, True,
- outFormat, createOpt, metaOpt)
+ alg.exportRasterLayer(output, fileName, True, outFormat, createOpt, metaOpt)
diff --git a/python/plugins/grassprovider/ext/r_tileset.py b/python/plugins/grassprovider/ext/r_tileset.py
index 15d8c8deb904..54de52025936 100644
--- a/python/plugins/grassprovider/ext/r_tileset.py
+++ b/python/plugins/grassprovider/ext/r_tileset.py
@@ -15,15 +15,15 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'October 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "October 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from grassprovider.grass_utils import GrassUtils
def processOutputs(alg, parameters, context, feedback):
- crs = alg.parameterAsCrs(parameters, 'sourceproj', context)
+ crs = alg.parameterAsCrs(parameters, "sourceproj", context)
wkt_file_name = GrassUtils.exportCrsWktToFile(crs, context)
- alg.commands.insert(0, 'g.proj -c wkt="{}"'.format(wkt_file_name))
+ alg.commands.insert(0, f'g.proj -c wkt="{wkt_file_name}"')
diff --git a/python/plugins/grassprovider/ext/r_what_color.py b/python/plugins/grassprovider/ext/r_what_color.py
index 9995e55922e4..ecf352a7935f 100644
--- a/python/plugins/grassprovider/ext/r_what_color.py
+++ b/python/plugins/grassprovider/ext/r_what_color.py
@@ -15,11 +15,11 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
# We need to import all the bands and color tables of the input rasters
- alg.loadRasterLayerFromParameter('input', parameters, context, False, None)
+ alg.loadRasterLayerFromParameter("input", parameters, context, False, None)
diff --git a/python/plugins/grassprovider/ext/v_distance.py b/python/plugins/grassprovider/ext/v_distance.py
index 7f1768d299cf..bf4ba6bf4d19 100644
--- a/python/plugins/grassprovider/ext/v_distance.py
+++ b/python/plugins/grassprovider/ext/v_distance.py
@@ -15,36 +15,42 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
from qgis.core import QgsProcessingParameterDefinition
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
+ """Verify if we have the right parameters"""
# Verifiy that we have the good number of columns
- uploads = alg.parameterAsEnums(parameters, 'upload', context)
- columns = alg.parameterAsFields(parameters, 'column', context)
+ uploads = alg.parameterAsEnums(parameters, "upload", context)
+ columns = alg.parameterAsFields(parameters, "column", context)
if len(columns) != len(uploads):
- return False, alg.tr("The number of columns and the number of upload parameters should be equal!")
+ return False, alg.tr(
+ "The number of columns and the number of upload parameters should be equal!"
+ )
return True, None
def processCommand(alg, parameters, context, feedback):
# We need to disable only from_output parameter
- fromOutput = alg.parameterDefinition('from_output')
- fromOutput.setFlags(fromOutput.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden)
+ fromOutput = alg.parameterDefinition("from_output")
+ fromOutput.setFlags(
+ fromOutput.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden
+ )
alg.processCommand(parameters, context, feedback, False)
- fromOutput.setFlags(fromOutput.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden)
+ fromOutput.setFlags(
+ fromOutput.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden
+ )
def processOutputs(alg, parameters, context, feedback):
alg.vectorOutputType(parameters, context)
- alg.exportVectorLayerFromParameter('output', parameters, context)
+ alg.exportVectorLayerFromParameter("output", parameters, context)
# for from_output, we export the initial layer
- fileName = alg.parameterAsOutputLayer(parameters, 'from_output', context)
- grassName = alg.exportedLayers['from']
+ fileName = alg.parameterAsOutputLayer(parameters, "from_output", context)
+ grassName = alg.exportedLayers["from"]
alg.exportVectorLayer(grassName, fileName)
diff --git a/python/plugins/grassprovider/ext/v_edit.py b/python/plugins/grassprovider/ext/v_edit.py
index 3091044ebd21..222741e13954 100644
--- a/python/plugins/grassprovider/ext/v_edit.py
+++ b/python/plugins/grassprovider/ext/v_edit.py
@@ -15,26 +15,29 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from processing.tools.system import getTempFilename
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- if (alg.parameterAsString(parameters, 'input_txt', context)
- and alg.parameterAsString(parameters, 'input', context)):
- return False, alg.tr("You need to set either an input ASCII file or inline data!")
+ """Verify if we have the right parameters"""
+ if alg.parameterAsString(
+ parameters, "input_txt", context
+ ) and alg.parameterAsString(parameters, "input", context):
+ return False, alg.tr(
+ "You need to set either an input ASCII file or inline data!"
+ )
return True, None
def processCommand(alg, parameters, context, feedback):
# Handle inline rules
- txtRules = alg.parameterAsString(parameters, 'input_txt', context)
+ txtRules = alg.parameterAsString(parameters, "input_txt", context)
if txtRules:
# Creates a temporary txt file
tempRulesName = getTempFilename(context=context)
@@ -42,15 +45,15 @@ def processCommand(alg, parameters, context, feedback):
# Inject rules into temporary txt file
with open(tempRulesName, "w") as tempRules:
tempRules.write(txtRules)
- alg.removeParameter('input_txt')
- parameters['input'] = tempRulesName
+ alg.removeParameter("input_txt")
+ parameters["input"] = tempRulesName
alg.processCommand(parameters, context, feedback, True)
def processOutputs(alg, parameters, context, feedback):
# We need to add the from layer to outputs:
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['map']
- dataType = 'auto'
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["map"]
+ dataType = "auto"
alg.exportVectorLayer(grassName, fileName, dataType=dataType)
diff --git a/python/plugins/grassprovider/ext/v_extrude.py b/python/plugins/grassprovider/ext/v_extrude.py
index 487eb451769f..8ceb33b50293 100644
--- a/python/plugins/grassprovider/ext/v_extrude.py
+++ b/python/plugins/grassprovider/ext/v_extrude.py
@@ -15,16 +15,18 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- height = alg.parameterAsDouble(parameters, 'height', context)
- height_column = alg.parameterAsString(parameters, 'height_column', context)
+ """Verify if we have the right parameters"""
+ height = alg.parameterAsDouble(parameters, "height", context)
+ height_column = alg.parameterAsString(parameters, "height_column", context)
if (height and height_column) or (not height and not height_column):
- return False, alg.tr("You need to set either a fixed height value or the height column!")
+ return False, alg.tr(
+ "You need to set either a fixed height value or the height column!"
+ )
return True, None
diff --git a/python/plugins/grassprovider/ext/v_in_geonames.py b/python/plugins/grassprovider/ext/v_in_geonames.py
index 569628f9b5e9..bd7ab4d38413 100644
--- a/python/plugins/grassprovider/ext/v_in_geonames.py
+++ b/python/plugins/grassprovider/ext/v_in_geonames.py
@@ -15,14 +15,14 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processCommand(alg, parameters, context, feedback):
# v.in.geonames needs to use WGS84 projection
- alg.commands.append('g.proj -c epsg=4326')
+ alg.commands.append("g.proj -c epsg=4326")
# Launch the algorithm
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/v_net.py b/python/plugins/grassprovider/ext/v_net.py
index 35d056a7dd2b..db4ac2513c4a 100644
--- a/python/plugins/grassprovider/ext/v_net.py
+++ b/python/plugins/grassprovider/ext/v_net.py
@@ -19,16 +19,23 @@
the network vector map.
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
import os
from qgis.core import QgsProcessingException
from processing.tools.system import getTempFilename
-def incorporatePoints(alg, parameters, context, feedback, pointLayerName='points', networkLayerName='input'):
+def incorporatePoints(
+ alg,
+ parameters,
+ context,
+ feedback,
+ pointLayerName="points",
+ networkLayerName="input",
+):
"""
incorporate points with lines to form a GRASS network
"""
@@ -37,7 +44,7 @@ def incorporatePoints(alg, parameters, context, feedback, pointLayerName='points
pointLayer = alg.parameterAsVectorLayer(parameters, pointLayerName, context)
if pointLayer:
# Create an intermediate GRASS layer which is the combination of network + centers
- intLayer = 'net' + os.path.basename(getTempFilename(context=context))
+ intLayer = "net" + os.path.basename(getTempFilename(context=context))
pointLayer = alg.exportedLayers[pointLayerName]
@@ -47,17 +54,19 @@ def incorporatePoints(alg, parameters, context, feedback, pointLayerName='points
lineLayer = alg.exportedLayers[networkLayerName]
else:
raise QgsProcessingException(
- alg.tr('GRASS GIS v.net requires a lines layer!'))
+ alg.tr("GRASS GIS v.net requires a lines layer!")
+ )
- threshold = alg.parameterAsDouble(parameters, 'threshold', context)
+ threshold = alg.parameterAsDouble(parameters, "threshold", context)
# Create the v.net connect command for point layer integration
- command = 'v.net -s input={} points={} output={} operation=connect threshold={}'.format(
- lineLayer, pointLayer, intLayer, threshold)
+ command = "v.net -s input={} points={} output={} operation=connect threshold={}".format(
+ lineLayer, pointLayer, intLayer, threshold
+ )
alg.commands.append(command)
# Connect the point layer database to the layer 2 of the network
- command = 'v.db.connect -o map={} table={} layer=2'.format(intLayer, pointLayer)
+ command = f"v.db.connect -o map={intLayer} table={pointLayer} layer=2"
alg.commands.append(command)
# remove undesired parameters
@@ -67,14 +76,14 @@ def incorporatePoints(alg, parameters, context, feedback, pointLayerName='points
alg.exportedLayers[networkLayerName] = intLayer
# Process the command
- if 'threshold' in parameters:
- alg.removeParameter('threshold')
+ if "threshold" in parameters:
+ alg.removeParameter("threshold")
alg.processCommand(parameters, context, feedback)
def variableOutput(alg, layers, parameters, context, nocats=True):
- """ Handle variable data output for v.net modules:
+ """Handle variable data output for v.net modules:
:param layers:
layers is a dict of outputs:
{ 'outputName': ['srcLayer', 'output_type', output_layer_number, nocats],
@@ -101,23 +110,25 @@ def variableOutput(alg, layers, parameters, context, nocats=True):
output_layer_number = typeList[2]
no_cats = typeList[3]
- grass_name = '{}{}'.format(src_layer, alg.uniqueSuffix)
- alg.exportVectorLayer(grassName=grass_name,
- fileName=file_name,
- layer=output_layer_number,
- exportnocat=no_cats,
- dataType=output_type)
+ grass_name = f"{src_layer}{alg.uniqueSuffix}"
+ alg.exportVectorLayer(
+ grassName=grass_name,
+ fileName=file_name,
+ layer=output_layer_number,
+ exportnocat=no_cats,
+ dataType=output_type,
+ )
def processOutputs(alg, parameters, context, feedback):
- idx = alg.parameterAsInt(parameters, 'operation', context)
- operations = alg.parameterDefinition('operation').options()
+ idx = alg.parameterAsInt(parameters, "operation", context)
+ operations = alg.parameterDefinition("operation").options()
operation = operations[idx]
- if operation == 'nodes':
- outputParameter = {'output': ['output', 'point', 2, True]}
- elif operation == 'connect':
- outputParameter = {'output': ['output', 'line', 1, False]}
- elif operation == 'arcs':
- outputParameter = {'output': ['output', 'line', 1, True]}
+ if operation == "nodes":
+ outputParameter = {"output": ["output", "point", 2, True]}
+ elif operation == "connect":
+ outputParameter = {"output": ["output", "line", 1, False]}
+ elif operation == "arcs":
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_alloc.py b/python/plugins/grassprovider/ext/v_net_alloc.py
index 10f0844a8767..0b4d6aa13ed4 100644
--- a/python/plugins/grassprovider/ext/v_net_alloc.py
+++ b/python/plugins/grassprovider/ext/v_net_alloc.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, False]}
+ outputParameter = {"output": ["output", "line", 1, False]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_allpairs.py b/python/plugins/grassprovider/ext/v_net_allpairs.py
index 43d23a668321..9823f7103442 100644
--- a/python/plugins/grassprovider/ext/v_net_allpairs.py
+++ b/python/plugins/grassprovider/ext/v_net_allpairs.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_bridge.py b/python/plugins/grassprovider/ext/v_net_bridge.py
index 6a8ac8f35114..4a418d8d0ba3 100644
--- a/python/plugins/grassprovider/ext/v_net_bridge.py
+++ b/python/plugins/grassprovider/ext/v_net_bridge.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,12 +27,12 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- idx = alg.parameterAsInt(parameters, 'method', context)
- operations = alg.parameterDefinition('method').options()
+ idx = alg.parameterAsInt(parameters, "method", context)
+ operations = alg.parameterDefinition("method").options()
operation = operations[idx]
- if operation == 'articulation':
- outputParameter = {'output': ['output', 'point', 2, True]}
- elif operation == 'bridge':
- outputParameter = {'output': ['output', 'line', 1, False]}
+ if operation == "articulation":
+ outputParameter = {"output": ["output", "point", 2, True]}
+ elif operation == "bridge":
+ outputParameter = {"output": ["output", "line", 1, False]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_centrality.py b/python/plugins/grassprovider/ext/v_net_centrality.py
index c3dfd92287ed..8f74e96e2c0b 100644
--- a/python/plugins/grassprovider/ext/v_net_centrality.py
+++ b/python/plugins/grassprovider/ext/v_net_centrality.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'point', 1, False]}
+ outputParameter = {"output": ["output", "point", 1, False]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_components.py b/python/plugins/grassprovider/ext/v_net_components.py
index a6c28f712cb2..d3580f4bc4b0 100644
--- a/python/plugins/grassprovider/ext/v_net_components.py
+++ b/python/plugins/grassprovider/ext/v_net_components.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
from qgis.core import QgsProcessingParameterDefinition
@@ -25,13 +25,19 @@
def processCommand(alg, parameters, context, feedback):
# We need to disable only output_point parameter
- outPoint = alg.parameterDefinition('output_point')
- outPoint.setFlags(outPoint.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden)
+ outPoint = alg.parameterDefinition("output_point")
+ outPoint.setFlags(
+ outPoint.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden
+ )
incorporatePoints(alg, parameters, context, feedback)
- outPoint.setFlags(outPoint.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden)
+ outPoint.setFlags(
+ outPoint.flags() | QgsProcessingParameterDefinition.Flag.FlagHidden
+ )
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True],
- 'output_point': ['output', 'point', 2, True]}
+ outputParameter = {
+ "output": ["output", "line", 1, True],
+ "output_point": ["output", "point", 2, True],
+ }
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_connectivity.py b/python/plugins/grassprovider/ext/v_net_connectivity.py
index 72e6d7494681..cf66bce732da 100644
--- a/python/plugins/grassprovider/ext/v_net_connectivity.py
+++ b/python/plugins/grassprovider/ext/v_net_connectivity.py
@@ -15,31 +15,27 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- params = ['where', 'cats']
+ """Verify if we have the right parameters"""
+ params = ["where", "cats"]
values = []
for param in params:
for i in range(1, 3):
- values.append(
- alg.parameterAsString(
- parameters,
- 'set{}_{}'.format(i, param),
- context
- )
- )
+ values.append(alg.parameterAsString(parameters, f"set{i}_{param}", context))
if (values[0] or values[2]) and (values[1] or values[3]):
return True, None
- return False, alg.tr('You need to set at least setX_where or setX_cats parameters for each set!')
+ return False, alg.tr(
+ "You need to set at least setX_where or setX_cats parameters for each set!"
+ )
def processCommand(alg, parameters, context, feedback):
@@ -47,5 +43,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'point', 2, True]}
+ outputParameter = {"output": ["output", "point", 2, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_distance.py b/python/plugins/grassprovider/ext/v_net_distance.py
index 574365fdab09..2ec7be3cacc6 100644
--- a/python/plugins/grassprovider/ext/v_net_distance.py
+++ b/python/plugins/grassprovider/ext/v_net_distance.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
import os
from .v_net import variableOutput
@@ -26,51 +26,53 @@
def processCommand(alg, parameters, context, feedback):
- """ Handle data preparation for v.net.distance:
+ """Handle data preparation for v.net.distance:
* Integrate point layers into network vector map.
* Make v.net.distance use those layers.
* Delete the threshold parameter.
* If where statement, connect to the db
"""
# Grab the point layer and delete this parameter
- lineLayer = alg.exportedLayers['input']
- fromLayer = alg.exportedLayers['flayer']
- toLayer = alg.exportedLayers['tlayer']
- intLayer = 'bufnet' + os.path.basename(getTempFilename(context=context))
- netLayer = 'net' + os.path.basename(getTempFilename(context=context))
- threshold = alg.parameterAsDouble(parameters, 'threshold', context)
+ lineLayer = alg.exportedLayers["input"]
+ fromLayer = alg.exportedLayers["flayer"]
+ toLayer = alg.exportedLayers["tlayer"]
+ intLayer = "bufnet" + os.path.basename(getTempFilename(context=context))
+ netLayer = "net" + os.path.basename(getTempFilename(context=context))
+ threshold = alg.parameterAsDouble(parameters, "threshold", context)
# Create the v.net connect command for from_layer integration
- command = 'v.net -s input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=2'.format(
- lineLayer, fromLayer, intLayer, threshold)
+ command = "v.net -s input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=2".format(
+ lineLayer, fromLayer, intLayer, threshold
+ )
alg.commands.append(command)
# Do it again with to_layer
- command = 'v.net -s input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=3'.format(
- intLayer, toLayer, netLayer, threshold)
+ command = "v.net -s input={} points={} output={} operation=connect threshold={} arc_layer=1 node_layer=3".format(
+ intLayer, toLayer, netLayer, threshold
+ )
alg.commands.append(command)
# Connect the point layer database to the layer 2 of the network
- command = 'v.db.connect -o map={} table={} layer=2'.format(netLayer, fromLayer)
+ command = f"v.db.connect -o map={netLayer} table={fromLayer} layer=2"
alg.commands.append(command)
- command = 'v.db.connect -o map={} table={} layer=3'.format(netLayer, toLayer)
+ command = f"v.db.connect -o map={netLayer} table={toLayer} layer=3"
alg.commands.append(command)
# remove undesired parameters
- alg.removeParameter('flayer')
- alg.removeParameter('tlayer')
- alg.removeParameter('threshold')
- alg.exportedLayers['input'] = netLayer
+ alg.removeParameter("flayer")
+ alg.removeParameter("tlayer")
+ alg.removeParameter("threshold")
+ alg.exportedLayers["input"] = netLayer
# Add the two new parameters
- fLayer = QgsProcessingParameterString('from_layer', None, 2, False, False)
+ fLayer = QgsProcessingParameterString("from_layer", None, 2, False, False)
alg.addParameter(fLayer)
- tLayer = QgsProcessingParameterString('to_layer', None, 3, False, False)
+ tLayer = QgsProcessingParameterString("to_layer", None, 3, False, False)
alg.addParameter(tLayer)
alg.processCommand(parameters, context, feedback)
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_flow.py b/python/plugins/grassprovider/ext/v_net_flow.py
index a0ada0fba081..efdf95f9374d 100644
--- a/python/plugins/grassprovider/ext/v_net_flow.py
+++ b/python/plugins/grassprovider/ext/v_net_flow.py
@@ -15,31 +15,27 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- params = ['where', 'cats']
+ """Verify if we have the right parameters"""
+ params = ["where", "cats"]
values = []
for param in params:
- for i in ['source', 'sink']:
- values.append(
- alg.parameterAsString(
- parameters,
- '{}_{}'.format(i, param),
- context
- )
- )
+ for i in ["source", "sink"]:
+ values.append(alg.parameterAsString(parameters, f"{i}_{param}", context))
if (values[0] or values[2]) and (values[1] or values[3]):
return True, None
- return False, alg.tr('You need to set at least source/sink_where or source/sink_cats parameters for each set!')
+ return False, alg.tr(
+ "You need to set at least source/sink_where or source/sink_cats parameters for each set!"
+ )
def processCommand(alg, parameters, context, feedback):
@@ -47,6 +43,8 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True],
- 'cut': ['cut', 'line', 1, True]}
+ outputParameter = {
+ "output": ["output", "line", 1, True],
+ "cut": ["cut", "line", 1, True],
+ }
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_iso.py b/python/plugins/grassprovider/ext/v_net_iso.py
index 3cc81b80a63a..1bb2277334b5 100644
--- a/python/plugins/grassprovider/ext/v_net_iso.py
+++ b/python/plugins/grassprovider/ext/v_net_iso.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_path.py b/python/plugins/grassprovider/ext/v_net_path.py
index eee13dae330f..6f9f3f19def2 100644
--- a/python/plugins/grassprovider/ext/v_net_path.py
+++ b/python/plugins/grassprovider/ext/v_net_path.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, False]}
+ outputParameter = {"output": ["output", "line", 1, False]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_salesman.py b/python/plugins/grassprovider/ext/v_net_salesman.py
index 9f802eb4b292..a774ccef4ec5 100644
--- a/python/plugins/grassprovider/ext/v_net_salesman.py
+++ b/python/plugins/grassprovider/ext/v_net_salesman.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
from qgis.core import QgsProcessingParameterDefinition
@@ -28,5 +28,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_spanningtree.py b/python/plugins/grassprovider/ext/v_net_spanningtree.py
index 20cd537dca95..2e9bee20ad42 100644
--- a/python/plugins/grassprovider/ext/v_net_spanningtree.py
+++ b/python/plugins/grassprovider/ext/v_net_spanningtree.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_steiner.py b/python/plugins/grassprovider/ext/v_net_steiner.py
index bcf524200831..1dab232b8417 100644
--- a/python/plugins/grassprovider/ext/v_net_steiner.py
+++ b/python/plugins/grassprovider/ext/v_net_steiner.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import incorporatePoints, variableOutput
@@ -27,5 +27,5 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, False]}
+ outputParameter = {"output": ["output", "line", 1, False]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_net_visibility.py b/python/plugins/grassprovider/ext/v_net_visibility.py
index 93eeb4836261..bb278002c480 100644
--- a/python/plugins/grassprovider/ext/v_net_visibility.py
+++ b/python/plugins/grassprovider/ext/v_net_visibility.py
@@ -15,13 +15,13 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2015'
-__copyright__ = '(C) 2015, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2015"
+__copyright__ = "(C) 2015, Médéric Ribreux"
from .v_net import variableOutput
def processOutputs(alg, parameters, context, feedback):
- outputParameter = {'output': ['output', 'line', 1, True]}
+ outputParameter = {"output": ["output", "line", 1, True]}
variableOutput(alg, outputParameter, parameters, context)
diff --git a/python/plugins/grassprovider/ext/v_proj.py b/python/plugins/grassprovider/ext/v_proj.py
index deb72bd93352..ce9b20134d1e 100644
--- a/python/plugins/grassprovider/ext/v_proj.py
+++ b/python/plugins/grassprovider/ext/v_proj.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'November 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "November 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
from qgis.core import QgsProcessingParameterString
from grassprovider.grass_utils import GrassUtils
@@ -25,39 +25,34 @@
def processInputs(alg, parameters, context, feedback):
# Grab the projection from the input vector layer
- layer = alg.parameterAsLayer(parameters, 'input', context)
+ layer = alg.parameterAsLayer(parameters, "input", context)
alg.setSessionProjectionFromLayer(layer, context)
layerCrs = layer.crs().toProj()
# Creates a new location with this Crs
wkt_file_name = GrassUtils.exportCrsWktToFile(layer.crs(), context)
- newLocation = 'newProj{}'.format(alg.uniqueSuffix)
- alg.commands.append('g.proj wkt="{}" location={}'.format(
- wkt_file_name, newLocation))
+ newLocation = f"newProj{alg.uniqueSuffix}"
+ alg.commands.append(f'g.proj wkt="{wkt_file_name}" location={newLocation}')
# Go to the newly created location
- alg.commands.append('g.mapset mapset=PERMANENT location={}'.format(
- newLocation))
+ alg.commands.append(f"g.mapset mapset=PERMANENT location={newLocation}")
# Import the layer
- alg.loadVectorLayerFromParameter(
- 'input', parameters, context, feedback, False)
+ alg.loadVectorLayerFromParameter("input", parameters, context, feedback, False)
# Go back to default location
- alg.commands.append('g.mapset mapset=PERMANENT location=temp_location')
+ alg.commands.append("g.mapset mapset=PERMANENT location=temp_location")
# Grab the projected Crs
- crs = alg.parameterAsCrs(parameters, 'crs', context)
+ crs = alg.parameterAsCrs(parameters, "crs", context)
wkt_file_name = GrassUtils.exportCrsWktToFile(crs, context)
- alg.commands.append('g.proj -c wkt="{}"'.format(wkt_file_name))
+ alg.commands.append(f'g.proj -c wkt="{wkt_file_name}"')
# Remove crs parameter
- alg.removeParameter('crs')
+ alg.removeParameter("crs")
# Add the location parameter with proper value
location = QgsProcessingParameterString(
- 'location',
- 'new location',
- 'newProj{}'.format(alg.uniqueSuffix)
+ "location", "new location", f"newProj{alg.uniqueSuffix}"
)
alg.addParameter(location)
diff --git a/python/plugins/grassprovider/ext/v_rast_stats.py b/python/plugins/grassprovider/ext/v_rast_stats.py
index 2f55a8f1e50a..f495979f4f57 100644
--- a/python/plugins/grassprovider/ext/v_rast_stats.py
+++ b/python/plugins/grassprovider/ext/v_rast_stats.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processCommand(alg, parameters, context, feedback):
@@ -27,7 +27,7 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# We need to add the initial vector layer to outputs:
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['map']
- dataType = 'auto'
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["map"]
+ dataType = "auto"
alg.exportVectorLayer(grassName, fileName, dataType=dataType)
diff --git a/python/plugins/grassprovider/ext/v_reclass.py b/python/plugins/grassprovider/ext/v_reclass.py
index 80bda61e43b1..f7d6996ca0a6 100644
--- a/python/plugins/grassprovider/ext/v_reclass.py
+++ b/python/plugins/grassprovider/ext/v_reclass.py
@@ -15,16 +15,16 @@
***************************************************************************
"""
-__author__ = 'Andrea Giudiceandrea'
-__date__ = 'June 2023'
-__copyright__ = '(C) 2023, Andrea Giudiceandrea'
+__author__ = "Andrea Giudiceandrea"
+__date__ = "June 2023"
+__copyright__ = "(C) 2023, Andrea Giudiceandrea"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
+ """Verify if we have the right parameters"""
# rules and column parameters are mutually exclusive
- rules = alg.parameterAsString(parameters, 'rules', context)
- column = alg.parameterAsString(parameters, 'column', context)
+ rules = alg.parameterAsString(parameters, "rules", context)
+ column = alg.parameterAsString(parameters, "column", context)
if (rules and column) or (not rules and not column):
return False, alg.tr("You need to set either a rules file or a column!")
diff --git a/python/plugins/grassprovider/ext/v_rectify.py b/python/plugins/grassprovider/ext/v_rectify.py
index ada7bbf1bcf1..fcaac2242856 100644
--- a/python/plugins/grassprovider/ext/v_rectify.py
+++ b/python/plugins/grassprovider/ext/v_rectify.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import os
from grassprovider.grass_utils import GrassUtils
@@ -25,17 +25,20 @@
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- if (alg.parameterAsString(parameters, 'inline_points', context)
- and alg.parameterAsString(parameters, 'points', context)):
- return False, alg.tr("You need to set either an input control point file or inline control points!")
+ """Verify if we have the right parameters"""
+ if alg.parameterAsString(
+ parameters, "inline_points", context
+ ) and alg.parameterAsString(parameters, "points", context):
+ return False, alg.tr(
+ "You need to set either an input control point file or inline control points!"
+ )
return True, None
def processCommand(alg, parameters, context, feedback):
# handle inline points
- inlinePoints = alg.parameterAsString(parameters, 'inline_points', context)
+ inlinePoints = alg.parameterAsString(parameters, "inline_points", context)
if inlinePoints:
# Creates a temporary txt file
pointsName = getTempFilename(context=context)
@@ -43,7 +46,7 @@ def processCommand(alg, parameters, context, feedback):
# Inject rules into temporary txt file
with open(pointsName, "w") as tempPoints:
tempPoints.write(inlinePoints)
- alg.removeParameter('inline_points')
- parameters['points'] = pointsName
+ alg.removeParameter("inline_points")
+ parameters["points"] = pointsName
alg.processCommand(parameters, context, feedback)
diff --git a/python/plugins/grassprovider/ext/v_sample.py b/python/plugins/grassprovider/ext/v_sample.py
index dfa5f98fdf06..69758c784318 100644
--- a/python/plugins/grassprovider/ext/v_sample.py
+++ b/python/plugins/grassprovider/ext/v_sample.py
@@ -15,17 +15,17 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
- if 'input' in alg.exportedLayers:
+ if "input" in alg.exportedLayers:
return
# We need to import the vector with v.in.ogr
# and we can use r.external for the raster
- alg.loadVectorLayerFromParameter('input', parameters, context, feedback, False)
- alg.loadRasterLayerFromParameter('raster', parameters, context, True)
+ alg.loadVectorLayerFromParameter("input", parameters, context, feedback, False)
+ alg.loadRasterLayerFromParameter("raster", parameters, context, True)
alg.postInputs(context)
diff --git a/python/plugins/grassprovider/ext/v_to_3d.py b/python/plugins/grassprovider/ext/v_to_3d.py
index fd13399993a6..fd8c4727e55f 100644
--- a/python/plugins/grassprovider/ext/v_to_3d.py
+++ b/python/plugins/grassprovider/ext/v_to_3d.py
@@ -15,25 +15,27 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
- height = alg.parameterAsDouble(parameters, 'height', context)
- column = alg.parameterAsString(parameters, 'column', context)
+ """Verify if we have the right parameters"""
+ height = alg.parameterAsDouble(parameters, "height", context)
+ column = alg.parameterAsString(parameters, "column", context)
if (height and column) or (not height and not column):
- return False, alg.tr("You need to set either a fixed height value or the height column!")
+ return False, alg.tr(
+ "You need to set either a fixed height value or the height column!"
+ )
return True, None
def processInputs(alg, parameters, context, feedback):
- if 'input' in alg.exportedLayers:
+ if "input" in alg.exportedLayers:
return
# We need to import the vector layer with v.in.ogr
- alg.loadVectorLayerFromParameter('input', parameters, context, feedback, False)
+ alg.loadVectorLayerFromParameter("input", parameters, context, feedback, False)
alg.postInputs(context)
diff --git a/python/plugins/grassprovider/ext/v_transform.py b/python/plugins/grassprovider/ext/v_transform.py
index e24b78e56407..f6287f73a12e 100644
--- a/python/plugins/grassprovider/ext/v_transform.py
+++ b/python/plugins/grassprovider/ext/v_transform.py
@@ -15,18 +15,20 @@
***************************************************************************
"""
-__author__ = 'Andrea Giudiceandrea'
-__date__ = 'February 2024'
-__copyright__ = '(C) 2024, Andrea Giudiceandrea'
+__author__ = "Andrea Giudiceandrea"
+__date__ = "February 2024"
+__copyright__ = "(C) 2024, Andrea Giudiceandrea"
def checkParameterValuesBeforeExecuting(alg, parameters, context):
- """ Verify if we have the right parameters """
+ """Verify if we have the right parameters"""
# -w, -x and -y parameters are mutually exclusive
- w = alg.parameterAsBoolean(parameters, '-w', context)
- x = alg.parameterAsBoolean(parameters, '-x', context)
- y = alg.parameterAsBoolean(parameters, '-y', context)
+ w = alg.parameterAsBoolean(parameters, "-w", context)
+ x = alg.parameterAsBoolean(parameters, "-x", context)
+ y = alg.parameterAsBoolean(parameters, "-y", context)
if sum([w, x, y]) > 1:
- return False, alg.tr("The 'Swap coordinates' parameters -w, -x and -y are mutually exclusive. You need to set either none or only one of them!")
+ return False, alg.tr(
+ "The 'Swap coordinates' parameters -w, -x and -y are mutually exclusive. You need to set either none or only one of them!"
+ )
return True, None
diff --git a/python/plugins/grassprovider/ext/v_vect_stats.py b/python/plugins/grassprovider/ext/v_vect_stats.py
index bc73f5bc80a5..2d3476849289 100644
--- a/python/plugins/grassprovider/ext/v_vect_stats.py
+++ b/python/plugins/grassprovider/ext/v_vect_stats.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processCommand(alg, parameters, context, feedback):
@@ -27,7 +27,7 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# We need to add the initial vector layer to outputs:
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['areas']
- dataType = 'auto'
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["areas"]
+ dataType = "auto"
alg.exportVectorLayer(grassName, fileName, dataType=dataType)
diff --git a/python/plugins/grassprovider/ext/v_voronoi.py b/python/plugins/grassprovider/ext/v_voronoi.py
index c57bea145744..8b0ac2ea60e3 100644
--- a/python/plugins/grassprovider/ext/v_voronoi.py
+++ b/python/plugins/grassprovider/ext/v_voronoi.py
@@ -15,26 +15,26 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'February 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "February 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processInputs(alg, parameters, context, feedback):
- if 'input' in alg.exportedLayers:
+ if "input" in alg.exportedLayers:
return
# We need to use v.in.ogr instead of v.external
- alg.loadVectorLayerFromParameter('input', parameters, context, feedback, False)
+ alg.loadVectorLayerFromParameter("input", parameters, context, feedback, False)
alg.processInputs(parameters, context, feedback)
def processOutputs(alg, parameters, context, feedback):
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = '{}{}'.format('output', alg.uniqueSuffix)
- dataType = 'auto'
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = "{}{}".format("output", alg.uniqueSuffix)
+ dataType = "auto"
# if we export a graph, output type will be a line
- if alg.parameterAsBoolean(parameters, '-l', context):
- dataType = 'line'
+ if alg.parameterAsBoolean(parameters, "-l", context):
+ dataType = "line"
alg.exportVectorLayer(grassName, fileName, dataType=dataType)
diff --git a/python/plugins/grassprovider/ext/v_what_rast.py b/python/plugins/grassprovider/ext/v_what_rast.py
index 170f7341adc1..d898bd556906 100644
--- a/python/plugins/grassprovider/ext/v_what_rast.py
+++ b/python/plugins/grassprovider/ext/v_what_rast.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'December 2017'
-__copyright__ = '(C) 2017, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "December 2017"
+__copyright__ = "(C) 2017, Médéric Ribreux"
def processCommand(alg, parameters, context, feedback):
@@ -27,7 +27,7 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# We need to add the initial vector layer to outputs:
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['map']
- dataType = 'auto'
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["map"]
+ dataType = "auto"
alg.exportVectorLayer(grassName, fileName, dataType=dataType)
diff --git a/python/plugins/grassprovider/ext/v_what_vect.py b/python/plugins/grassprovider/ext/v_what_vect.py
index 242ae9844639..18ca0224d420 100644
--- a/python/plugins/grassprovider/ext/v_what_vect.py
+++ b/python/plugins/grassprovider/ext/v_what_vect.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'March 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "March 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
def processCommand(alg, parameters, context, feedback):
@@ -27,6 +27,6 @@ def processCommand(alg, parameters, context, feedback):
def processOutputs(alg, parameters, context, feedback):
# We need to add the initial vector layer to outputs:
- fileName = alg.parameterAsOutputLayer(parameters, 'output', context)
- grassName = alg.exportedLayers['map']
+ fileName = alg.parameterAsOutputLayer(parameters, "output", context)
+ grassName = alg.exportedLayers["map"]
alg.exportVectorLayer(grassName, fileName)
diff --git a/python/plugins/grassprovider/grass_algorithm.py b/python/plugins/grassprovider/grass_algorithm.py
index 0556335b05e2..1f2eb3aec09d 100644
--- a/python/plugins/grassprovider/grass_algorithm.py
+++ b/python/plugins/grassprovider/grass_algorithm.py
@@ -15,14 +15,11 @@
***************************************************************************
"""
-__author__ = 'Victor Olaya'
-__date__ = 'February 2015'
-__copyright__ = '(C) 2012-2015, Victor Olaya'
+__author__ = "Victor Olaya"
+__date__ = "February 2015"
+__copyright__ = "(C) 2012-2015, Victor Olaya"
-from typing import (
- Dict,
- Optional
-)
+from typing import Dict, Optional
import sys
import os
import uuid
@@ -32,41 +29,43 @@
from qgis.PyQt.QtCore import QCoreApplication, QUrl
-from qgis.core import (Qgis,
- QgsMapLayer,
- QgsRasterLayer,
- QgsApplication,
- QgsMapLayerType,
- QgsCoordinateReferenceSystem,
- QgsProcessingUtils,
- QgsProcessing,
- QgsMessageLog,
- QgsVectorFileWriter,
- QgsProcessingContext,
- QgsProcessingAlgorithm,
- QgsProcessingParameterDefinition,
- QgsProcessingException,
- QgsProcessingParameterCrs,
- QgsProcessingParameterExtent,
- QgsProcessingParameterEnum,
- QgsProcessingParameterNumber,
- QgsProcessingParameterString,
- QgsProcessingParameterField,
- QgsProcessingParameterPoint,
- QgsProcessingParameterBoolean,
- QgsProcessingParameterRange,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterVectorLayer,
- QgsProcessingParameterRasterLayer,
- QgsProcessingParameterMultipleLayers,
- QgsProcessingParameterVectorDestination,
- QgsProcessingParameterRasterDestination,
- QgsProcessingParameterFileDestination,
- QgsProcessingParameterFile,
- QgsProcessingParameterFolderDestination,
- QgsProcessingOutputHtml,
- QgsVectorLayer,
- QgsProviderRegistry)
+from qgis.core import (
+ Qgis,
+ QgsMapLayer,
+ QgsRasterLayer,
+ QgsApplication,
+ QgsMapLayerType,
+ QgsCoordinateReferenceSystem,
+ QgsProcessingUtils,
+ QgsProcessing,
+ QgsMessageLog,
+ QgsVectorFileWriter,
+ QgsProcessingContext,
+ QgsProcessingAlgorithm,
+ QgsProcessingParameterDefinition,
+ QgsProcessingException,
+ QgsProcessingParameterCrs,
+ QgsProcessingParameterExtent,
+ QgsProcessingParameterEnum,
+ QgsProcessingParameterNumber,
+ QgsProcessingParameterString,
+ QgsProcessingParameterField,
+ QgsProcessingParameterPoint,
+ QgsProcessingParameterBoolean,
+ QgsProcessingParameterRange,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterVectorLayer,
+ QgsProcessingParameterRasterLayer,
+ QgsProcessingParameterMultipleLayers,
+ QgsProcessingParameterVectorDestination,
+ QgsProcessingParameterRasterDestination,
+ QgsProcessingParameterFileDestination,
+ QgsProcessingParameterFile,
+ QgsProcessingParameterFolderDestination,
+ QgsProcessingOutputHtml,
+ QgsVectorLayer,
+ QgsProviderRegistry,
+)
from qgis.utils import iface
import warnings
@@ -83,41 +82,45 @@
from processing.tools.system import isWindows, getTempFilename
-pluginPath = os.path.normpath(os.path.join(
- os.path.split(os.path.dirname(__file__))[0], os.pardir))
+pluginPath = os.path.normpath(
+ os.path.join(os.path.split(os.path.dirname(__file__))[0], os.pardir)
+)
class GrassAlgorithm(QgsProcessingAlgorithm):
- GRASS_OUTPUT_TYPE_PARAMETER = 'GRASS_OUTPUT_TYPE_PARAMETER'
- GRASS_MIN_AREA_PARAMETER = 'GRASS_MIN_AREA_PARAMETER'
- GRASS_SNAP_TOLERANCE_PARAMETER = 'GRASS_SNAP_TOLERANCE_PARAMETER'
- GRASS_REGION_EXTENT_PARAMETER = 'GRASS_REGION_PARAMETER'
- GRASS_REGION_CELLSIZE_PARAMETER = 'GRASS_REGION_CELLSIZE_PARAMETER'
- GRASS_REGION_ALIGN_TO_RESOLUTION = 'GRASS_REGION_ALIGN_TO_RESOLUTION'
- GRASS_RASTER_FORMAT_OPT = 'GRASS_RASTER_FORMAT_OPT'
- GRASS_RASTER_FORMAT_META = 'GRASS_RASTER_FORMAT_META'
- GRASS_VECTOR_DSCO = 'GRASS_VECTOR_DSCO'
- GRASS_VECTOR_LCO = 'GRASS_VECTOR_LCO'
- GRASS_VECTOR_EXPORT_NOCAT = 'GRASS_VECTOR_EXPORT_NOCAT'
-
- OUTPUT_TYPES = ['auto', 'point', 'line', 'area']
- QGIS_OUTPUT_TYPES = {QgsProcessing.SourceType.TypeVectorAnyGeometry: 'auto',
- QgsProcessing.SourceType.TypeVectorPoint: 'point',
- QgsProcessing.SourceType.TypeVectorLine: 'line',
- QgsProcessing.SourceType.TypeVectorPolygon: 'area'}
-
- def __init__(self,
- description_file: Optional[Path] = None,
- json_definition: Optional[Dict] = None,
- description_folder: Optional[Path] = None
- ):
+ GRASS_OUTPUT_TYPE_PARAMETER = "GRASS_OUTPUT_TYPE_PARAMETER"
+ GRASS_MIN_AREA_PARAMETER = "GRASS_MIN_AREA_PARAMETER"
+ GRASS_SNAP_TOLERANCE_PARAMETER = "GRASS_SNAP_TOLERANCE_PARAMETER"
+ GRASS_REGION_EXTENT_PARAMETER = "GRASS_REGION_PARAMETER"
+ GRASS_REGION_CELLSIZE_PARAMETER = "GRASS_REGION_CELLSIZE_PARAMETER"
+ GRASS_REGION_ALIGN_TO_RESOLUTION = "GRASS_REGION_ALIGN_TO_RESOLUTION"
+ GRASS_RASTER_FORMAT_OPT = "GRASS_RASTER_FORMAT_OPT"
+ GRASS_RASTER_FORMAT_META = "GRASS_RASTER_FORMAT_META"
+ GRASS_VECTOR_DSCO = "GRASS_VECTOR_DSCO"
+ GRASS_VECTOR_LCO = "GRASS_VECTOR_LCO"
+ GRASS_VECTOR_EXPORT_NOCAT = "GRASS_VECTOR_EXPORT_NOCAT"
+
+ OUTPUT_TYPES = ["auto", "point", "line", "area"]
+ QGIS_OUTPUT_TYPES = {
+ QgsProcessing.SourceType.TypeVectorAnyGeometry: "auto",
+ QgsProcessing.SourceType.TypeVectorPoint: "point",
+ QgsProcessing.SourceType.TypeVectorLine: "line",
+ QgsProcessing.SourceType.TypeVectorPolygon: "area",
+ }
+
+ def __init__(
+ self,
+ description_file: Optional[Path] = None,
+ json_definition: Optional[dict] = None,
+ description_folder: Optional[Path] = None,
+ ):
super().__init__()
- self._name = ''
- self._display_name = ''
- self._short_description = ''
- self._group = ''
- self._groupId = ''
- self.grass_name = ''
+ self._name = ""
+ self._display_name = ""
+ self._short_description = ""
+ self._group = ""
+ self._groupId = ""
+ self.grass_name = ""
self.params = []
self.hardcodedStrings = []
self.inputLayers = []
@@ -126,7 +129,7 @@ def __init__(self,
self.exportedLayers = {}
self.fileOutputs = {}
self._description_file: Optional[Path] = description_file
- self._json_definition: Optional[Dict] = json_definition
+ self._json_definition: Optional[dict] = json_definition
self._description_folder: Optional[Path] = description_folder
# Default GRASS parameters
@@ -148,7 +151,7 @@ def __init__(self,
self.numExportedLayers = 0
# Do we need this anymore?
- self.uniqueSuffix = str(uuid.uuid4()).replace('-', '')
+ self.uniqueSuffix = str(uuid.uuid4()).replace("-", "")
# Use the ext mechanism
self.module = None
@@ -156,29 +159,38 @@ def __init__(self,
extpath = None
ext_name = None
if self._description_file:
- ext_name = self.name().replace('.', '_')
- extpath = self._description_file.parents[1].joinpath('ext', ext_name + '.py')
- elif self._json_definition.get('ext_path'):
- ext_name = self._json_definition['ext_path']
+ ext_name = self.name().replace(".", "_")
+ extpath = self._description_file.parents[1].joinpath(
+ "ext", ext_name + ".py"
+ )
+ elif self._json_definition.get("ext_path"):
+ ext_name = self._json_definition["ext_path"]
extpath = self._description_folder.parents[0].joinpath(
- 'ext', ext_name + '.py')
+ "ext", ext_name + ".py"
+ )
# this check makes it a bit faster
if extpath and extpath.exists():
spec = importlib.util.spec_from_file_location(
- 'grassprovider.ext.' + ext_name, extpath)
+ "grassprovider.ext." + ext_name, extpath
+ )
self.module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(self.module)
except Exception as e:
- QgsMessageLog.logMessage(self.tr('Failed to load: {0}\n{1}').format(extpath, e), 'Processing', Qgis.MessageLevel.Critical)
+ QgsMessageLog.logMessage(
+ self.tr("Failed to load: {0}\n{1}").format(extpath, e),
+ "Processing",
+ Qgis.MessageLevel.Critical,
+ )
pass
def createInstance(self):
return self.__class__(
description_file=self._description_file,
json_definition=self._json_definition,
- description_folder=self._description_folder)
+ description_folder=self._description_folder,
+ )
def name(self):
return self._name
@@ -203,22 +215,28 @@ def svgIconPath(self):
def flags(self):
# TODO - maybe it's safe to background thread this?
- return super().flags() | QgsProcessingAlgorithm.Flag.FlagNoThreading | QgsProcessingAlgorithm.Flag.FlagDisplayNameIsLiteral
+ return (
+ super().flags()
+ | QgsProcessingAlgorithm.Flag.FlagNoThreading
+ | QgsProcessingAlgorithm.Flag.FlagDisplayNameIsLiteral
+ )
- def tr(self, string, context=''):
- if context == '':
+ def tr(self, string, context=""):
+ if context == "":
context = self.__class__.__name__
return QCoreApplication.translate(context, string)
def helpUrl(self):
helpPath = GrassUtils.grassHelpPath()
- if helpPath == '':
+ if helpPath == "":
return None
if os.path.exists(helpPath):
- return QUrl.fromLocalFile(os.path.join(helpPath, '{}.html'.format(self.grass_name))).toString()
+ return QUrl.fromLocalFile(
+ os.path.join(helpPath, f"{self.grass_name}.html")
+ ).toString()
else:
- return helpPath + '{}.html'.format(self.grass_name)
+ return helpPath + f"{self.grass_name}.html"
def initAlgorithm(self, config=None):
"""
@@ -232,25 +250,19 @@ def _define_characteristics_from_file(self):
"""
Create algorithm parameters and outputs from a text file.
"""
- results = ParsedDescription.parse_description_file(
- self._description_file)
- self._define_characteristics_from_parsed_description(
- results
- )
+ results = ParsedDescription.parse_description_file(self._description_file)
+ self._define_characteristics_from_parsed_description(results)
def _define_characteristics_from_json(self):
"""
Create algorithm parameters and outputs from JSON definition.
"""
- results = ParsedDescription.from_dict(
- self._json_definition)
- self._define_characteristics_from_parsed_description(
- results
- )
+ results = ParsedDescription.from_dict(self._json_definition)
+ self._define_characteristics_from_parsed_description(results)
def _define_characteristics_from_parsed_description(
- self,
- description: ParsedDescription):
+ self, description: ParsedDescription
+ ):
"""
Create algorithm parameters and outputs from parsed description
"""
@@ -275,128 +287,164 @@ def _define_characteristics_from_parsed_description(
parameter = getParameterFromString(param_string, "GrassAlgorithm")
except Exception as e:
QgsMessageLog.logMessage(
- QCoreApplication.translate("GrassAlgorithm",
- 'Could not open GRASS GIS algorithm: {0}').format(
- self._name),
- QCoreApplication.translate("GrassAlgorithm",
- 'Processing'),
- Qgis.MessageLevel.Critical)
+ QCoreApplication.translate(
+ "GrassAlgorithm", "Could not open GRASS GIS algorithm: {0}"
+ ).format(self._name),
+ QCoreApplication.translate("GrassAlgorithm", "Processing"),
+ Qgis.MessageLevel.Critical,
+ )
raise e
if parameter is None:
continue
self.params.append(parameter)
- if isinstance(parameter, (
+ if isinstance(
+ parameter,
+ (
QgsProcessingParameterVectorLayer,
- QgsProcessingParameterFeatureSource)):
+ QgsProcessingParameterFeatureSource,
+ ),
+ ):
has_vector_input = True
- elif isinstance(parameter,
- QgsProcessingParameterRasterLayer):
+ elif isinstance(parameter, QgsProcessingParameterRasterLayer):
has_raster_input = True
- elif isinstance(parameter,
- QgsProcessingParameterMultipleLayers):
+ elif isinstance(parameter, QgsProcessingParameterMultipleLayers):
if parameter.layerType() < 3 or parameter.layerType() == 5:
has_vector_input = True
elif parameter.layerType() == 3:
has_raster_input = True
- elif isinstance(parameter,
- QgsProcessingParameterVectorDestination):
+ elif isinstance(parameter, QgsProcessingParameterVectorDestination):
has_vector_outputs = True
- elif isinstance(parameter,
- QgsProcessingParameterRasterDestination):
+ elif isinstance(parameter, QgsProcessingParameterRasterDestination):
has_raster_output = True
param = QgsProcessingParameterExtent(
self.GRASS_REGION_EXTENT_PARAMETER,
- self.tr('GRASS GIS region extent'),
- optional=True
+ self.tr("GRASS GIS region extent"),
+ optional=True,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
self.params.append(param)
if has_raster_output or has_raster_input:
# Add a cellsize parameter
param = QgsProcessingParameterNumber(
self.GRASS_REGION_CELLSIZE_PARAMETER,
- self.tr('GRASS GIS region cellsize (leave 0 for default)'),
+ self.tr("GRASS GIS region cellsize (leave 0 for default)"),
type=QgsProcessingParameterNumber.Type.Double,
- minValue=0.0, maxValue=sys.float_info.max + 1, defaultValue=0.0
+ minValue=0.0,
+ maxValue=sys.float_info.max + 1,
+ defaultValue=0.0,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
self.params.append(param)
if has_raster_output:
# Add a createopt parameter for format export
param = QgsProcessingParameterString(
self.GRASS_RASTER_FORMAT_OPT,
- self.tr('Output Rasters format options (createopt)'),
- multiLine=True, optional=True
+ self.tr("Output Rasters format options (createopt)"),
+ multiLine=True,
+ optional=True,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
- param.setHelp(self.tr('Creation options should be comma separated'))
+ param.setHelp(self.tr("Creation options should be comma separated"))
self.params.append(param)
# Add a metadata parameter for format export
param = QgsProcessingParameterString(
self.GRASS_RASTER_FORMAT_META,
- self.tr('Output Rasters format metadata options (metaopt)'),
- multiLine=True, optional=True
+ self.tr("Output Rasters format metadata options (metaopt)"),
+ multiLine=True,
+ optional=True,
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
- param.setHelp(self.tr('Metadata options should be comma separated'))
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
+ )
+ param.setHelp(self.tr("Metadata options should be comma separated"))
self.params.append(param)
if has_vector_input:
- param = QgsProcessingParameterNumber(self.GRASS_SNAP_TOLERANCE_PARAMETER,
- self.tr('v.in.ogr snap tolerance (-1 = no snap)'),
- type=QgsProcessingParameterNumber.Type.Double,
- minValue=-1.0, maxValue=sys.float_info.max + 1,
- defaultValue=-1.0)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
+ param = QgsProcessingParameterNumber(
+ self.GRASS_SNAP_TOLERANCE_PARAMETER,
+ self.tr("v.in.ogr snap tolerance (-1 = no snap)"),
+ type=QgsProcessingParameterNumber.Type.Double,
+ minValue=-1.0,
+ maxValue=sys.float_info.max + 1,
+ defaultValue=-1.0,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
+ )
self.params.append(param)
- param = QgsProcessingParameterNumber(self.GRASS_MIN_AREA_PARAMETER,
- self.tr('v.in.ogr min area'),
- type=QgsProcessingParameterNumber.Type.Double,
- minValue=0.0, maxValue=sys.float_info.max + 1,
- defaultValue=0.0001)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
+ param = QgsProcessingParameterNumber(
+ self.GRASS_MIN_AREA_PARAMETER,
+ self.tr("v.in.ogr min area"),
+ type=QgsProcessingParameterNumber.Type.Double,
+ minValue=0.0,
+ maxValue=sys.float_info.max + 1,
+ defaultValue=0.0001,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
+ )
self.params.append(param)
if has_vector_outputs:
# Add an optional output type
- param = QgsProcessingParameterEnum(self.GRASS_OUTPUT_TYPE_PARAMETER,
- self.tr('v.out.ogr output type'),
- self.OUTPUT_TYPES,
- defaultValue=0)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
+ param = QgsProcessingParameterEnum(
+ self.GRASS_OUTPUT_TYPE_PARAMETER,
+ self.tr("v.out.ogr output type"),
+ self.OUTPUT_TYPES,
+ defaultValue=0,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
+ )
self.params.append(param)
# Add a DSCO parameter for format export
param = QgsProcessingParameterString(
self.GRASS_VECTOR_DSCO,
- self.tr('v.out.ogr output data source options (dsco)'),
- multiLine=True, optional=True
+ self.tr("v.out.ogr output data source options (dsco)"),
+ multiLine=True,
+ optional=True,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
self.params.append(param)
# Add a LCO parameter for format export
param = QgsProcessingParameterString(
self.GRASS_VECTOR_LCO,
- self.tr('v.out.ogr output layer options (lco)'),
- multiLine=True, optional=True
+ self.tr("v.out.ogr output layer options (lco)"),
+ multiLine=True,
+ optional=True,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
self.params.append(param)
# Add a -c flag for export
param = QgsProcessingParameterBoolean(
self.GRASS_VECTOR_EXPORT_NOCAT,
- self.tr('Also export features without category (not labeled). Otherwise only features with category are exported'),
- False
+ self.tr(
+ "Also export features without category (not labeled). Otherwise only features with category are exported"
+ ),
+ False,
+ )
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
)
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced)
self.params.append(param)
def getDefaultCellSize(self):
@@ -420,38 +468,41 @@ def grabDefaultGrassParameters(self, parameters, context):
object attributes for faster retrieving.
"""
# GRASS region extent
- self.region = self.parameterAsExtent(parameters,
- self.GRASS_REGION_EXTENT_PARAMETER,
- context)
+ self.region = self.parameterAsExtent(
+ parameters, self.GRASS_REGION_EXTENT_PARAMETER, context
+ )
# GRASS cell size
if self.parameterDefinition(self.GRASS_REGION_CELLSIZE_PARAMETER):
- self.cellSize = self.parameterAsDouble(parameters,
- self.GRASS_REGION_CELLSIZE_PARAMETER,
- context)
+ self.cellSize = self.parameterAsDouble(
+ parameters, self.GRASS_REGION_CELLSIZE_PARAMETER, context
+ )
# GRASS snap tolerance
- self.snapTolerance = self.parameterAsDouble(parameters,
- self.GRASS_SNAP_TOLERANCE_PARAMETER,
- context)
+ self.snapTolerance = self.parameterAsDouble(
+ parameters, self.GRASS_SNAP_TOLERANCE_PARAMETER, context
+ )
# GRASS min area
- self.minArea = self.parameterAsDouble(parameters,
- self.GRASS_MIN_AREA_PARAMETER,
- context)
+ self.minArea = self.parameterAsDouble(
+ parameters, self.GRASS_MIN_AREA_PARAMETER, context
+ )
# GRASS output type
- self.outputType = self.parameterAsString(parameters,
- self.GRASS_OUTPUT_TYPE_PARAMETER,
- context)
+ self.outputType = self.parameterAsString(
+ parameters, self.GRASS_OUTPUT_TYPE_PARAMETER, context
+ )
# GRASS align to resolution
- self.alignToResolution = self.parameterAsBoolean(parameters,
- self.GRASS_REGION_ALIGN_TO_RESOLUTION,
- context)
+ self.alignToResolution = self.parameterAsBoolean(
+ parameters, self.GRASS_REGION_ALIGN_TO_RESOLUTION, context
+ )
def processAlgorithm(self, original_parameters, context, feedback):
if isWindows():
path = GrassUtils.grassPath()
- if path == '':
+ if path == "":
raise QgsProcessingException(
- self.tr('GRASS GIS folder is not configured. Please '
- 'configure it before running GRASS GIS algorithms.'))
+ self.tr(
+ "GRASS GIS folder is not configured. Please "
+ "configure it before running GRASS GIS algorithms."
+ )
+ )
# make a copy of the original parameters dictionary - it gets modified by grass algorithms
parameters = {k: v for k, v in original_parameters.items()}
@@ -475,20 +526,22 @@ def processAlgorithm(self, original_parameters, context, feedback):
self.grabDefaultGrassParameters(parameters, context)
# Handle ext functions for inputs/command/outputs
- for fName in ['Inputs', 'Command', 'Outputs']:
- fullName = 'process{}'.format(fName)
+ for fName in ["Inputs", "Command", "Outputs"]:
+ fullName = f"process{fName}"
if self.module and hasattr(self.module, fullName):
getattr(self.module, fullName)(self, parameters, context, feedback)
else:
getattr(self, fullName)(parameters, context, feedback)
# Run GRASS
- loglines = [self.tr('GRASS GIS execution commands')]
+ loglines = [self.tr("GRASS GIS execution commands")]
for line in self.commands:
feedback.pushCommandInfo(line)
loglines.append(line)
if ProcessingConfig.getSetting(GrassUtils.GRASS_LOG_COMMANDS):
- QgsMessageLog.logMessage("\n".join(loglines), self.tr('Processing'), Qgis.MessageLevel.Info)
+ QgsMessageLog.logMessage(
+ "\n".join(loglines), self.tr("Processing"), Qgis.MessageLevel.Info
+ )
GrassUtils.executeGrass(self.commands, feedback, self.outputCommands)
@@ -509,8 +562,8 @@ def processAlgorithm(self, original_parameters, context, feedback):
else:
outputs[outName] = parameters[outName]
if isinstance(out, QgsProcessingOutputHtml):
- if self.module and hasattr(self.module, 'convertToHtml'):
- func = getattr(self.module, 'convertToHtml')
+ if self.module and hasattr(self.module, "convertToHtml"):
+ func = getattr(self.module, "convertToHtml")
func(self, self.fileOutputs[outName], outputs)
else:
self.convertToHtml(self.fileOutputs[outName])
@@ -518,11 +571,19 @@ def processAlgorithm(self, original_parameters, context, feedback):
def processInputs(self, parameters, context, feedback):
"""Prepare the GRASS import commands"""
- inputs = [p for p in self.parameterDefinitions()
- if isinstance(p, (QgsProcessingParameterVectorLayer,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterRasterLayer,
- QgsProcessingParameterMultipleLayers))]
+ inputs = [
+ p
+ for p in self.parameterDefinitions()
+ if isinstance(
+ p,
+ (
+ QgsProcessingParameterVectorLayer,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterRasterLayer,
+ QgsProcessingParameterMultipleLayers,
+ ),
+ )
+ ]
for param in inputs:
paramName = param.name()
if paramName not in parameters:
@@ -530,35 +591,51 @@ def processInputs(self, parameters, context, feedback):
# Handle Null parameter
if parameters[paramName] is None:
continue
- elif isinstance(parameters[paramName], str) and len(parameters[paramName]) == 0:
+ elif (
+ isinstance(parameters[paramName], str)
+ and len(parameters[paramName]) == 0
+ ):
continue
# Raster inputs needs to be imported into temp GRASS DB
if isinstance(param, QgsProcessingParameterRasterLayer):
if paramName not in self.exportedLayers:
- self.loadRasterLayerFromParameter(
- paramName, parameters, context)
+ self.loadRasterLayerFromParameter(paramName, parameters, context)
# Vector inputs needs to be imported into temp GRASS DB
- elif isinstance(param, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)):
+ elif isinstance(
+ param,
+ (
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterVectorLayer,
+ ),
+ ):
if paramName not in self.exportedLayers:
# Attribute tables are also vector inputs
if QgsProcessing.SourceType.TypeFile in param.dataTypes():
self.loadAttributeTableFromParameter(
- paramName, parameters, context)
+ paramName, parameters, context
+ )
else:
self.loadVectorLayerFromParameter(
- paramName, parameters, context, external=None, feedback=feedback)
+ paramName,
+ parameters,
+ context,
+ external=None,
+ feedback=feedback,
+ )
# For multiple inputs, process each layer
elif isinstance(param, QgsProcessingParameterMultipleLayers):
layers = self.parameterAsLayerList(parameters, paramName, context)
for idx, layer in enumerate(layers):
- layerName = '{}_{}'.format(paramName, idx)
+ layerName = f"{paramName}_{idx}"
# Add a raster layer
if layer.type() == QgsMapLayerType.RasterLayer:
self.loadRasterLayer(layerName, layer, context)
# Add a vector layer
elif layer.type() == QgsMapLayerType.VectorLayer:
- self.loadVectorLayer(layerName, layer, context, external=None, feedback=feedback)
+ self.loadVectorLayer(
+ layerName, layer, context, external=None, feedback=feedback
+ )
self.postInputs(context)
@@ -571,10 +648,14 @@ def postInputs(self, context: QgsProcessingContext):
# Build GRASS region
if self.region.isEmpty():
- self.region = QgsProcessingUtils.combineLayerExtents(self.inputLayers, self.destination_crs, context)
- command = 'g.region n={} s={} e={} w={}'.format(
- self.region.yMaximum(), self.region.yMinimum(),
- self.region.xMaximum(), self.region.xMinimum()
+ self.region = QgsProcessingUtils.combineLayerExtents(
+ self.inputLayers, self.destination_crs, context
+ )
+ command = "g.region n={} s={} e={} w={}".format(
+ self.region.yMaximum(),
+ self.region.yMinimum(),
+ self.region.xMaximum(),
+ self.region.xMinimum(),
)
# Handle cell size
if self.parameterDefinition(self.GRASS_REGION_CELLSIZE_PARAMETER):
@@ -582,16 +663,20 @@ def postInputs(self, context: QgsProcessingContext):
cellSize = self.cellSize
else:
cellSize = self.getDefaultCellSize()
- command += ' res={}'.format(cellSize)
+ command += f" res={cellSize}"
# Handle align to resolution
if self.alignToResolution:
- command += ' -a'
+ command += " -a"
# Add the default parameters commands
self.commands.append(command)
- QgsMessageLog.logMessage(self.tr('processInputs end. Commands: {}').format(self.commands), 'Grass', Qgis.MessageLevel.Info)
+ QgsMessageLog.logMessage(
+ self.tr("processInputs end. Commands: {}").format(self.commands),
+ "Grass",
+ Qgis.MessageLevel.Info,
+ )
def processCommand(self, parameters, context, feedback, delOutputs=False):
"""
@@ -600,9 +685,13 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
:param context:
:param delOutputs: do not add outputs to commands.
"""
- noOutputs = [o for o in self.parameterDefinitions() if o not in self.destinationParameterDefinitions()]
- command = '{} '.format(self.grass_name)
- command += '{}'.join(self.hardcodedStrings)
+ noOutputs = [
+ o
+ for o in self.parameterDefinitions()
+ if o not in self.destinationParameterDefinitions()
+ ]
+ command = f"{self.grass_name} "
+ command += "{}".join(self.hardcodedStrings)
# Add algorithm command
for param in noOutputs:
@@ -610,42 +699,51 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
value = None
# Exclude default GRASS parameters
- if paramName in [self.GRASS_REGION_CELLSIZE_PARAMETER,
- self.GRASS_REGION_EXTENT_PARAMETER,
- self.GRASS_MIN_AREA_PARAMETER,
- self.GRASS_SNAP_TOLERANCE_PARAMETER,
- self.GRASS_OUTPUT_TYPE_PARAMETER,
- self.GRASS_REGION_ALIGN_TO_RESOLUTION,
- self.GRASS_RASTER_FORMAT_OPT,
- self.GRASS_RASTER_FORMAT_META,
- self.GRASS_VECTOR_DSCO,
- self.GRASS_VECTOR_LCO,
- self.GRASS_VECTOR_EXPORT_NOCAT]:
+ if paramName in [
+ self.GRASS_REGION_CELLSIZE_PARAMETER,
+ self.GRASS_REGION_EXTENT_PARAMETER,
+ self.GRASS_MIN_AREA_PARAMETER,
+ self.GRASS_SNAP_TOLERANCE_PARAMETER,
+ self.GRASS_OUTPUT_TYPE_PARAMETER,
+ self.GRASS_REGION_ALIGN_TO_RESOLUTION,
+ self.GRASS_RASTER_FORMAT_OPT,
+ self.GRASS_RASTER_FORMAT_META,
+ self.GRASS_VECTOR_DSCO,
+ self.GRASS_VECTOR_LCO,
+ self.GRASS_VECTOR_EXPORT_NOCAT,
+ ]:
continue
# Raster and vector layers
- if isinstance(param, (QgsProcessingParameterRasterLayer,
- QgsProcessingParameterVectorLayer,
- QgsProcessingParameterFeatureSource)):
+ if isinstance(
+ param,
+ (
+ QgsProcessingParameterRasterLayer,
+ QgsProcessingParameterVectorLayer,
+ QgsProcessingParameterFeatureSource,
+ ),
+ ):
if paramName in self.exportedLayers:
value = self.exportedLayers[paramName]
else:
value = self.parameterAsCompatibleSourceLayerPath(
- parameters, paramName, context,
- QgsVectorFileWriter.supportedFormatExtensions()
+ parameters,
+ paramName,
+ context,
+ QgsVectorFileWriter.supportedFormatExtensions(),
)
# MultipleLayers
elif isinstance(param, QgsProcessingParameterMultipleLayers):
layers = self.parameterAsLayerList(parameters, paramName, context)
values = []
for idx in range(len(layers)):
- layerName = '{}_{}'.format(paramName, idx)
+ layerName = f"{paramName}_{idx}"
values.append(self.exportedLayers[layerName])
- value = ','.join(values)
+ value = ",".join(values)
# For booleans, we just add the parameter name
elif isinstance(param, QgsProcessingParameterBoolean):
if self.parameterAsBoolean(parameters, paramName, context):
- command += ' {}'.format(paramName)
+ command += f" {paramName}"
# For Extents, remove if the value is null
elif isinstance(param, QgsProcessingParameterExtent):
if self.parameterAsExtent(parameters, paramName, context):
@@ -658,7 +756,9 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
else:
indexes = [self.parameterAsEnum(parameters, paramName, context)]
if indexes:
- value = '"{}"'.format(','.join([param.options()[i] for i in indexes]))
+ value = '"{}"'.format(
+ ",".join([param.options()[i] for i in indexes])
+ )
# For strings, we just translate as string
elif isinstance(param, QgsProcessingParameterString):
data = self.parameterAsString(parameters, paramName, context)
@@ -669,9 +769,7 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
)
# For fields, we just translate as string
elif isinstance(param, QgsProcessingParameterField):
- value = ','.join(
- self.parameterAsFields(parameters, paramName, context)
- )
+ value = ",".join(self.parameterAsFields(parameters, paramName, context))
elif isinstance(param, QgsProcessingParameterFile):
if self.parameterAsString(parameters, paramName, context):
value = '"{}"'.format(
@@ -682,29 +780,32 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
# parameter specified, evaluate as point
# TODO - handle CRS transform
point = self.parameterAsPoint(parameters, paramName, context)
- value = '{},{}'.format(point.x(), point.y())
+ value = f"{point.x()},{point.y()}"
# For numbers, we translate as a string
- elif isinstance(param, (QgsProcessingParameterNumber,
- QgsProcessingParameterPoint)):
+ elif isinstance(
+ param, (QgsProcessingParameterNumber, QgsProcessingParameterPoint)
+ ):
value = self.parameterAsString(parameters, paramName, context)
elif isinstance(param, QgsProcessingParameterRange):
v = self.parameterAsRange(parameters, paramName, context)
- if (param.flags() & QgsProcessingParameterDefinition.Flag.FlagOptional) and (math.isnan(v[0]) or math.isnan(v[1])):
+ if (
+ param.flags() & QgsProcessingParameterDefinition.Flag.FlagOptional
+ ) and (math.isnan(v[0]) or math.isnan(v[1])):
continue
else:
- value = '{},{}'.format(v[0], v[1])
+ value = f"{v[0]},{v[1]}"
elif isinstance(param, QgsProcessingParameterCrs):
if self.parameterAsCrs(parameters, paramName, context):
# TODO: ideally we should be exporting to WKT here, but it seems not all grass algorithms
# will accept a wkt string for a crs value (e.g. r.tileset)
- value = '"{}"'.format(self.parameterAsCrs(parameters, paramName, context).toProj())
+ value = f'"{self.parameterAsCrs(parameters, paramName, context).toProj()}"'
# For everything else, we assume that it is a string
else:
value = '"{}"'.format(
self.parameterAsString(parameters, paramName, context)
)
if value:
- command += ' {}={}'.format(paramName.replace('~', ''), value)
+ command += " {}={}".format(paramName.replace("~", ""), value)
# Handle outputs
if not delOutputs:
@@ -716,47 +817,52 @@ def processCommand(self, parameters, context, feedback, delOutputs=False):
# For File destination
if isinstance(out, QgsProcessingParameterFileDestination):
if outName in parameters and parameters[outName] is not None:
- outPath = self.parameterAsFileOutput(parameters, outName, context)
+ outPath = self.parameterAsFileOutput(
+ parameters, outName, context
+ )
self.fileOutputs[outName] = outPath
# for HTML reports, we need to redirect stdout
- if out.defaultFileExtension().lower() == 'html':
- if outName == 'html':
+ if out.defaultFileExtension().lower() == "html":
+ if outName == "html":
# for "fake" outputs redirect command stdout
- command += ' > "{}"'.format(outPath)
+ command += f' > "{outPath}"'
else:
# for real outputs only output itself should be redirected
- command += ' {}=- > "{}"'.format(outName, outPath)
+ command += f' {outName}=- > "{outPath}"'
else:
- command += ' {}="{}"'.format(outName, outPath)
+ command += f' {outName}="{outPath}"'
# For folders destination
elif isinstance(out, QgsProcessingParameterFolderDestination):
# We need to add a unique temporary basename
uniqueBasename = outName + self.uniqueSuffix
- command += ' {}={}'.format(outName, uniqueBasename)
+ command += f" {outName}={uniqueBasename}"
else:
if outName in parameters and parameters[outName] is not None:
# We add an output name to make sure it is unique if the session
# uses this algorithm several times.
uniqueOutputName = outName + self.uniqueSuffix
- command += ' {}={}'.format(outName, uniqueOutputName)
+ command += f" {outName}={uniqueOutputName}"
# Add output file to exported layers, to indicate that
# they are present in GRASS
self.exportedLayers[outName] = uniqueOutputName
- command += ' --overwrite'
+ command += " --overwrite"
self.commands.append(command)
- QgsMessageLog.logMessage(self.tr('processCommands end. Commands: {}').format(self.commands), 'Grass', Qgis.MessageLevel.Info)
+ QgsMessageLog.logMessage(
+ self.tr("processCommands end. Commands: {}").format(self.commands),
+ "Grass",
+ Qgis.MessageLevel.Info,
+ )
def vectorOutputType(self, parameters, context):
"""Determine vector output types for outputs"""
- self.outType = 'auto'
+ self.outType = "auto"
if self.parameterDefinition(self.GRASS_OUTPUT_TYPE_PARAMETER):
- typeidx = self.parameterAsEnum(parameters,
- self.GRASS_OUTPUT_TYPE_PARAMETER,
- context)
- self.outType = ('auto' if typeidx
- is None else self.OUTPUT_TYPES[typeidx])
+ typeidx = self.parameterAsEnum(
+ parameters, self.GRASS_OUTPUT_TYPE_PARAMETER, context
+ )
+ self.outType = "auto" if typeidx is None else self.OUTPUT_TYPES[typeidx]
def processOutputs(self, parameters, context, feedback):
"""Prepare the GRASS v.out.ogr commands"""
@@ -776,9 +882,9 @@ def processOutputs(self, parameters, context, feedback):
elif isinstance(out, QgsProcessingParameterFolderDestination):
self.exportRasterLayersIntoDirectory(outName, parameters, context)
- def loadRasterLayerFromParameter(self, name, parameters,
- context: QgsProcessingContext,
- external=None, band=1):
+ def loadRasterLayerFromParameter(
+ self, name, parameters, context: QgsProcessingContext, external=None, band=1
+ ):
"""
Creates a dedicated command to load a raster into
the temporary GRASS DB.
@@ -791,8 +897,15 @@ def loadRasterLayerFromParameter(self, name, parameters,
layer = self.parameterAsRasterLayer(parameters, name, context)
self.loadRasterLayer(name, layer, context, external, band)
- def loadRasterLayer(self, name, layer, context: QgsProcessingContext,
- external=None, band=1, destName=None):
+ def loadRasterLayer(
+ self,
+ name,
+ layer,
+ context: QgsProcessingContext,
+ external=None,
+ band=1,
+ destName=None,
+ ):
"""
Creates a dedicated command to load a raster into
the temporary GRASS DB.
@@ -808,16 +921,19 @@ def loadRasterLayer(self, name, layer, context: QgsProcessingContext,
self.inputLayers.append(layer)
self.setSessionProjectionFromLayer(layer, context)
if not destName:
- destName = 'rast_{}'.format(os.path.basename(getTempFilename(context=context)))
+ destName = f"rast_{os.path.basename(getTempFilename(context=context))}"
self.exportedLayers[name] = destName
command = '{} input="{}" {}output="{}" --overwrite -o'.format(
- 'r.external' if external else 'r.in.gdal',
+ "r.external" if external else "r.in.gdal",
os.path.normpath(layer.source()),
- 'band={} '.format(band) if band else '',
- destName)
+ f"band={band} " if band else "",
+ destName,
+ )
self.commands.append(command)
- def exportRasterLayerFromParameter(self, name, parameters, context, colorTable=True):
+ def exportRasterLayerFromParameter(
+ self, name, parameters, context, colorTable=True
+ ):
"""
Creates a dedicated command to export a raster from
temporary GRASS DB into a file via gdal.
@@ -831,18 +947,29 @@ def exportRasterLayerFromParameter(self, name, parameters, context, colorTable=T
return
fileName = os.path.normpath(fileName)
- grassName = '{}{}'.format(name, self.uniqueSuffix)
+ grassName = f"{name}{self.uniqueSuffix}"
outFormat = GrassUtils.getRasterFormatFromFilename(fileName)
- createOpt = self.parameterAsString(parameters, self.GRASS_RASTER_FORMAT_OPT, context)
- metaOpt = self.parameterAsString(parameters, self.GRASS_RASTER_FORMAT_META, context)
- self.exportRasterLayer(grassName, fileName, colorTable, outFormat, createOpt, metaOpt)
+ createOpt = self.parameterAsString(
+ parameters, self.GRASS_RASTER_FORMAT_OPT, context
+ )
+ metaOpt = self.parameterAsString(
+ parameters, self.GRASS_RASTER_FORMAT_META, context
+ )
+ self.exportRasterLayer(
+ grassName, fileName, colorTable, outFormat, createOpt, metaOpt
+ )
self.fileOutputs[name] = fileName
- def exportRasterLayer(self, grassName, fileName,
- colorTable=True, outFormat='GTiff',
- createOpt=None,
- metaOpt=None):
+ def exportRasterLayer(
+ self,
+ grassName,
+ fileName,
+ colorTable=True,
+ outFormat="GTiff",
+ createOpt=None,
+ metaOpt=None,
+ ):
"""
Creates a dedicated command to export a raster from
temporary GRASS DB into a file via gdal.
@@ -853,22 +980,27 @@ def exportRasterLayer(self, grassName, fileName,
:param createOpt: creation options for format.
:param metaOpt: metadata options for export.
"""
- createOpt = createOpt or GrassUtils.GRASS_RASTER_FORMATS_CREATEOPTS.get(outFormat)
+ createOpt = createOpt or GrassUtils.GRASS_RASTER_FORMATS_CREATEOPTS.get(
+ outFormat
+ )
for cmd in [self.commands, self.outputCommands]:
# Adjust region to layer before exporting
- cmd.append('g.region raster={}'.format(grassName))
+ cmd.append(f"g.region raster={grassName}")
cmd.append(
'r.out.gdal -t -m{} input="{}" output="{}" format="{}" {}{} --overwrite'.format(
- '' if colorTable else ' -c',
- grassName, fileName,
+ "" if colorTable else " -c",
+ grassName,
+ fileName,
outFormat,
- ' createopt="{}"'.format(createOpt) if createOpt else '',
- ' metaopt="{}"'.format(metaOpt) if metaOpt else ''
+ f' createopt="{createOpt}"' if createOpt else "",
+ f' metaopt="{metaOpt}"' if metaOpt else "",
)
)
- def exportRasterLayersIntoDirectory(self, name, parameters, context, colorTable=True, wholeDB=False):
+ def exportRasterLayersIntoDirectory(
+ self, name, parameters, context, colorTable=True, wholeDB=False
+ ):
"""
Creates a dedicated loop command to export rasters from
temporary GRASS DB into a directory via gdal.
@@ -879,9 +1011,8 @@ def exportRasterLayersIntoDirectory(self, name, parameters, context, colorTable=
:param wholeDB: export every raster layer from the GRASSDB
"""
# Grab directory name and temporary basename
- outDir = os.path.normpath(
- self.parameterAsString(parameters, name, context))
- basename = ''
+ outDir = os.path.normpath(self.parameterAsString(parameters, name, context))
+ basename = ""
if not wholeDB:
basename = name + self.uniqueSuffix
@@ -890,21 +1021,28 @@ def exportRasterLayersIntoDirectory(self, name, parameters, context, colorTable=
# TODO Format/options support
if isWindows():
cmd.append("if not exist {0} mkdir {0}".format(outDir))
- cmd.append("for /F %%r IN ('g.list type^=rast pattern^=\"{}*\"') do r.out.gdal -m{} input=%%r output={}/%%r.tif {}".format(
- basename,
- ' -t' if colorTable else '',
- outDir,
- '--overwrite -c createopt="TFW=YES,COMPRESS=LZW"'
- ))
+ cmd.append(
+ "for /F %%r IN ('g.list type^=rast pattern^=\"{}*\"') do r.out.gdal -m{} input=%%r output={}/%%r.tif {}".format(
+ basename,
+ " -t" if colorTable else "",
+ outDir,
+ '--overwrite -c createopt="TFW=YES,COMPRESS=LZW"',
+ )
+ )
else:
- cmd.append("for r in $(g.list type=rast pattern='{}*'); do".format(basename))
- cmd.append(" r.out.gdal -m{0} input=${{r}} output={1}/${{r}}.tif {2}".format(
- ' -t' if colorTable else '', outDir,
- '--overwrite -c createopt="TFW=YES,COMPRESS=LZW"'
- ))
+ cmd.append(f"for r in $(g.list type=rast pattern='{basename}*'); do")
+ cmd.append(
+ " r.out.gdal -m{0} input=${{r}} output={1}/${{r}}.tif {2}".format(
+ " -t" if colorTable else "",
+ outDir,
+ '--overwrite -c createopt="TFW=YES,COMPRESS=LZW"',
+ )
+ )
cmd.append("done")
- def loadVectorLayerFromParameter(self, name, parameters, context, feedback, external=False):
+ def loadVectorLayerFromParameter(
+ self, name, parameters, context, feedback, external=False
+ ):
"""
Creates a dedicated command to load a vector into
the temporary GRASS DB.
@@ -915,31 +1053,43 @@ def loadVectorLayerFromParameter(self, name, parameters, context, feedback, exte
"""
layer = self.parameterAsVectorLayer(parameters, name, context)
- is_ogr_disk_based_layer = layer is not None and layer.dataProvider().name() == 'ogr'
+ is_ogr_disk_based_layer = (
+ layer is not None and layer.dataProvider().name() == "ogr"
+ )
if is_ogr_disk_based_layer:
# we only support direct reading of disk based ogr layers -- not ogr postgres layers, etc
- source_parts = QgsProviderRegistry.instance().decodeUri('ogr', layer.source())
- if not source_parts.get('path'):
+ source_parts = QgsProviderRegistry.instance().decodeUri(
+ "ogr", layer.source()
+ )
+ if not source_parts.get("path"):
is_ogr_disk_based_layer = False
- elif source_parts.get('layerId'):
+ elif source_parts.get("layerId"):
# no support for directly reading layers by id in grass
is_ogr_disk_based_layer = False
if not is_ogr_disk_based_layer:
# parameter is not a vector layer or not an OGR layer - try to convert to a source compatible with
# grass OGR inputs and extract selection if required
- path = self.parameterAsCompatibleSourceLayerPath(parameters, name, context,
- QgsVectorFileWriter.supportedFormatExtensions(),
- feedback=feedback)
- ogr_layer = QgsVectorLayer(path, '', 'ogr')
- self.loadVectorLayer(name, ogr_layer, context, external=external, feedback=feedback)
+ path = self.parameterAsCompatibleSourceLayerPath(
+ parameters,
+ name,
+ context,
+ QgsVectorFileWriter.supportedFormatExtensions(),
+ feedback=feedback,
+ )
+ ogr_layer = QgsVectorLayer(path, "", "ogr")
+ self.loadVectorLayer(
+ name, ogr_layer, context, external=external, feedback=feedback
+ )
else:
# already an ogr disk based layer source
- self.loadVectorLayer(name, layer, context, external=external, feedback=feedback)
+ self.loadVectorLayer(
+ name, layer, context, external=external, feedback=feedback
+ )
- def loadVectorLayer(self, name, layer,
- context: QgsProcessingContext,
- external=False, feedback=None):
+ def loadVectorLayer(
+ self, name, layer, context: QgsProcessingContext, external=False, feedback=None
+ ):
"""
Creates a dedicated command to load a vector into
temporary GRASS DB.
@@ -951,47 +1101,57 @@ def loadVectorLayer(self, name, layer,
"""
# TODO: support multiple input formats
if external is None:
- external = ProcessingConfig.getSetting(
- GrassUtils.GRASS_USE_VEXTERNAL)
+ external = ProcessingConfig.getSetting(GrassUtils.GRASS_USE_VEXTERNAL)
- source_parts = QgsProviderRegistry.instance().decodeUri('ogr', layer.source())
- file_path = source_parts.get('path')
- layer_name = source_parts.get('layerName')
+ source_parts = QgsProviderRegistry.instance().decodeUri("ogr", layer.source())
+ file_path = source_parts.get("path")
+ layer_name = source_parts.get("layerName")
# safety check: we can only use external for ogr layers which support random read
if external:
if feedback is not None:
- feedback.pushInfo(self.tr('Attempting to use v.external for direct layer read'))
+ feedback.pushInfo(
+ self.tr("Attempting to use v.external for direct layer read")
+ )
ds = ogr.Open(file_path)
if ds is not None:
ogr_layer = ds.GetLayer()
if ogr_layer is None or not ogr_layer.TestCapability(ogr.OLCRandomRead):
if feedback is not None:
- feedback.reportError(self.tr('Cannot use v.external: layer does not support random read'))
+ feedback.reportError(
+ self.tr(
+ "Cannot use v.external: layer does not support random read"
+ )
+ )
external = False
else:
if feedback is not None:
- feedback.reportError(self.tr('Cannot use v.external: error reading layer'))
+ feedback.reportError(
+ self.tr("Cannot use v.external: error reading layer")
+ )
external = False
self.inputLayers.append(layer)
self.setSessionProjectionFromLayer(layer, context)
- destFilename = 'vector_{}'.format(os.path.basename(getTempFilename(context=context)))
+ destFilename = f"vector_{os.path.basename(getTempFilename(context=context))}"
self.exportedLayers[name] = destFilename
command = '{}{}{} input="{}"{} output="{}" --overwrite -o'.format(
- 'v.external' if external else 'v.in.ogr',
- ' min_area={}'.format(self.minArea) if not external else '',
- ' snap={}'.format(self.snapTolerance) if not external else '',
+ "v.external" if external else "v.in.ogr",
+ f" min_area={self.minArea}" if not external else "",
+ f" snap={self.snapTolerance}" if not external else "",
os.path.normpath(file_path),
- ' layer="{}"'.format(layer_name) if layer_name else '',
- destFilename)
+ f' layer="{layer_name}"' if layer_name else "",
+ destFilename,
+ )
if layer.subsetString():
escaped_subset = layer.subsetString().replace('"', '\\"')
command += f' where="{escaped_subset}"'
self.commands.append(command)
- def exportVectorLayerFromParameter(self, name, parameters, context, layer=None, nocats=False):
+ def exportVectorLayerFromParameter(
+ self, name, parameters, context, layer=None, nocats=False
+ ):
"""
Creates a dedicated command to export a vector from
a QgsProcessingParameter.
@@ -1001,28 +1161,53 @@ def exportVectorLayerFromParameter(self, name, parameters, context, layer=None,
:param nocats: do not export GRASS categories.
"""
fileName = os.path.normpath(
- self.parameterAsOutputLayer(parameters, name, context))
- grassName = '{}{}'.format(name, self.uniqueSuffix)
+ self.parameterAsOutputLayer(parameters, name, context)
+ )
+ grassName = f"{name}{self.uniqueSuffix}"
# Find if there is a dataType
dataType = self.outType
- if self.outType == 'auto':
+ if self.outType == "auto":
parameter = self.parameterDefinition(name)
if parameter:
layerType = parameter.dataType()
if layerType in self.QGIS_OUTPUT_TYPES:
dataType = self.QGIS_OUTPUT_TYPES[layerType]
- outFormat = QgsVectorFileWriter.driverForExtension(os.path.splitext(fileName)[1]).replace(' ', '_')
+ outFormat = QgsVectorFileWriter.driverForExtension(
+ os.path.splitext(fileName)[1]
+ ).replace(" ", "_")
dsco = self.parameterAsString(parameters, self.GRASS_VECTOR_DSCO, context)
lco = self.parameterAsString(parameters, self.GRASS_VECTOR_LCO, context)
- exportnocat = self.parameterAsBoolean(parameters, self.GRASS_VECTOR_EXPORT_NOCAT, context)
- self.exportVectorLayer(grassName, fileName, layer, nocats, dataType, outFormat, dsco, lco, exportnocat)
+ exportnocat = self.parameterAsBoolean(
+ parameters, self.GRASS_VECTOR_EXPORT_NOCAT, context
+ )
+ self.exportVectorLayer(
+ grassName,
+ fileName,
+ layer,
+ nocats,
+ dataType,
+ outFormat,
+ dsco,
+ lco,
+ exportnocat,
+ )
self.fileOutputs[name] = fileName
- def exportVectorLayer(self, grassName, fileName, layer=None, nocats=False, dataType='auto',
- outFormat=None, dsco=None, lco=None, exportnocat=False):
+ def exportVectorLayer(
+ self,
+ grassName,
+ fileName,
+ layer=None,
+ nocats=False,
+ dataType="auto",
+ outFormat=None,
+ dsco=None,
+ lco=None,
+ exportnocat=False,
+ ):
"""
Creates a dedicated command to export a vector from
temporary GRASS DB into a file via OGR.
@@ -1037,18 +1222,22 @@ def exportVectorLayer(self, grassName, fileName, layer=None, nocats=False, dataT
:param exportnocat: do not export features without categories.
"""
if outFormat is None:
- outFormat = QgsVectorFileWriter.driverForExtension(os.path.splitext(fileName)[1]).replace(' ', '_')
+ outFormat = QgsVectorFileWriter.driverForExtension(
+ os.path.splitext(fileName)[1]
+ ).replace(" ", "_")
for cmd in [self.commands, self.outputCommands]:
cmd.append(
'v.out.ogr{} type="{}" input="{}" output="{}" format="{}" {}{}{}{} --overwrite'.format(
- '' if nocats else '',
- dataType, grassName, fileName,
+ "" if nocats else "",
+ dataType,
+ grassName,
+ fileName,
outFormat,
- 'layer={}'.format(layer) if layer else '',
- ' dsco="{}"'.format(dsco) if dsco else '',
- ' lco="{}"'.format(lco) if lco else '',
- ' -c' if exportnocat else ''
+ f"layer={layer}" if layer else "",
+ f' dsco="{dsco}"' if dsco else "",
+ f' lco="{lco}"' if lco else "",
+ " -c" if exportnocat else "",
)
)
@@ -1063,8 +1252,9 @@ def loadAttributeTableFromParameter(self, name, parameters, context):
table = self.parameterAsVectorLayer(parameters, name, context)
self.loadAttributeTable(name, table, context)
- def loadAttributeTable(self, name, layer, context: QgsProcessingContext,
- destName=None):
+ def loadAttributeTable(
+ self, name, layer, context: QgsProcessingContext, destName=None
+ ):
"""
Creates a dedicated command to load an attribute table
into the temporary GRASS DB.
@@ -1075,13 +1265,14 @@ def loadAttributeTable(self, name, layer, context: QgsProcessingContext,
"""
self.inputLayers.append(layer)
if not destName:
- destName = 'table_{}'.format(os.path.basename(getTempFilename(context=context)))
+ destName = f"table_{os.path.basename(getTempFilename(context=context))}"
self.exportedLayers[name] = destName
command = 'db.in.ogr --overwrite input="{}" output="{}"'.format(
- os.path.normpath(layer.source()), destName)
+ os.path.normpath(layer.source()), destName
+ )
self.commands.append(command)
- def exportAttributeTable(self, grassName, fileName, outFormat='CSV', layer=1):
+ def exportAttributeTable(self, grassName, fileName, outFormat="CSV", layer=1):
"""
Creates a dedicated command to export an attribute
table from the temporary GRASS DB into a file via ogr.
@@ -1104,11 +1295,12 @@ def setSessionProjectionFromProject(self, context: QgsProcessingContext):
"""
if not GrassUtils.projectionSet and iface:
self.setSessionProjection(
- iface.mapCanvas().mapSettings().destinationCrs(),
- context
+ iface.mapCanvas().mapSettings().destinationCrs(), context
)
- def setSessionProjectionFromLayer(self, layer: QgsMapLayer, context: QgsProcessingContext):
+ def setSessionProjectionFromLayer(
+ self, layer: QgsMapLayer, context: QgsProcessingContext
+ ):
"""
Set the projection from a QgsVectorLayer.
We create a WKT definition which is transmitted to Grass
@@ -1122,25 +1314,27 @@ def setSessionProjection(self, crs, context: QgsProcessingContext):
"""
self.destination_crs = crs
file_name = GrassUtils.exportCrsWktToFile(crs, context)
- command = 'g.proj -c wkt="{}"'.format(file_name)
+ command = f'g.proj -c wkt="{file_name}"'
self.commands.append(command)
GrassUtils.projectionSet = True
def convertToHtml(self, fileName):
# Read HTML contents
lines = []
- with open(fileName, encoding='utf-8') as f:
+ with open(fileName, encoding="utf-8") as f:
lines = f.readlines()
- if len(lines) > 1 and '' not in lines[0]:
+ if len(lines) > 1 and "" not in lines[0]:
# Then write into the HTML file
- with open(fileName, 'w', encoding='utf-8') as f:
- f.write('')
- f.write('')
- f.write('
')
+ with open(fileName, "w", encoding="utf-8") as f:
+ f.write("
")
+ f.write(
+ ''
+ )
+ f.write("
")
for line in lines:
- f.write('{}'.format(line))
- f.write('
')
+ f.write(f"{line}")
+ f.write("")
def canExecute(self):
message = GrassUtils.checkGrassIsInstalled()
@@ -1149,7 +1343,7 @@ def canExecute(self):
def checkParameterValues(self, parameters, context):
grass_parameters = {k: v for k, v in parameters.items()}
if self.module:
- if hasattr(self.module, 'checkParameterValuesBeforeExecuting'):
- func = getattr(self.module, 'checkParameterValuesBeforeExecuting')
+ if hasattr(self.module, "checkParameterValuesBeforeExecuting"):
+ func = getattr(self.module, "checkParameterValuesBeforeExecuting")
return func(self, grass_parameters, context)
return super().checkParameterValues(grass_parameters, context)
diff --git a/python/plugins/grassprovider/grass_plugin.py b/python/plugins/grassprovider/grass_plugin.py
index 1f82de63e7f9..7bf0c99f75b7 100644
--- a/python/plugins/grassprovider/grass_plugin.py
+++ b/python/plugins/grassprovider/grass_plugin.py
@@ -15,14 +15,14 @@
***************************************************************************
"""
-__author__ = 'Alexander Bruy'
-__date__ = 'June 2021'
-__copyright__ = '(C) 2021, Alexander Bruy'
+__author__ = "Alexander Bruy"
+__date__ = "June 2021"
+__copyright__ = "(C) 2021, Alexander Bruy"
from qgis.core import QgsApplication, QgsRuntimeProfiler
-with QgsRuntimeProfiler.profile('Import GRASS Provider'):
+with QgsRuntimeProfiler.profile("Import GRASS Provider"):
from grassprovider.grass_provider import GrassProvider
diff --git a/python/plugins/grassprovider/grass_provider.py b/python/plugins/grassprovider/grass_provider.py
index a71769397659..19022c0d08cc 100644
--- a/python/plugins/grassprovider/grass_provider.py
+++ b/python/plugins/grassprovider/grass_provider.py
@@ -15,21 +15,23 @@
***************************************************************************
"""
-__author__ = 'Victor Olaya'
-__date__ = 'April 2014'
-__copyright__ = '(C) 2014, Victor Olaya'
+__author__ = "Victor Olaya"
+__date__ = "April 2014"
+__copyright__ = "(C) 2014, Victor Olaya"
import json
from typing import List
from qgis.PyQt.QtCore import QCoreApplication
-from qgis.core import (Qgis,
- QgsApplication,
- QgsProcessingAlgorithm,
- QgsProcessingProvider,
- QgsVectorFileWriter,
- QgsMessageLog,
- QgsRuntimeProfiler)
-from processing.core.ProcessingConfig import (ProcessingConfig, Setting)
+from qgis.core import (
+ Qgis,
+ QgsApplication,
+ QgsProcessingAlgorithm,
+ QgsProcessingProvider,
+ QgsVectorFileWriter,
+ QgsMessageLog,
+ QgsRuntimeProfiler,
+)
+from processing.core.ProcessingConfig import ProcessingConfig, Setting
from grassprovider.grass_utils import GrassUtils
from grassprovider.grass_algorithm import GrassAlgorithm
@@ -40,35 +42,57 @@ def __init__(self):
super().__init__()
def load(self):
- with QgsRuntimeProfiler.profile('Grass Provider'):
+ with QgsRuntimeProfiler.profile("Grass Provider"):
ProcessingConfig.settingIcons[self.name()] = self.icon()
- ProcessingConfig.addSetting(Setting(
- self.name(),
- GrassUtils.GRASS_LOG_COMMANDS,
- self.tr('Log execution commands'), False))
- ProcessingConfig.addSetting(Setting(
- self.name(),
- GrassUtils.GRASS_LOG_CONSOLE,
- self.tr('Log console output'), False))
- ProcessingConfig.addSetting(Setting(
- self.name(),
- GrassUtils.GRASS_HELP_URL,
- self.tr('Location of GRASS docs'), ''))
+ ProcessingConfig.addSetting(
+ Setting(
+ self.name(),
+ GrassUtils.GRASS_LOG_COMMANDS,
+ self.tr("Log execution commands"),
+ False,
+ )
+ )
+ ProcessingConfig.addSetting(
+ Setting(
+ self.name(),
+ GrassUtils.GRASS_LOG_CONSOLE,
+ self.tr("Log console output"),
+ False,
+ )
+ )
+ ProcessingConfig.addSetting(
+ Setting(
+ self.name(),
+ GrassUtils.GRASS_HELP_URL,
+ self.tr("Location of GRASS docs"),
+ "",
+ )
+ )
# Add settings for using r.external/v.external instead of r.in.gdal/v.in.ogr
# but set them to False by default because the {r,v}.external implementations
# have some bugs on windows + there are algorithms that can't be used with
# external data (need a solid r.in.gdal/v.in.ogr).
# For more info have a look at e.g. https://trac.osgeo.org/grass/ticket/3927
- ProcessingConfig.addSetting(Setting(
- self.name(),
- GrassUtils.GRASS_USE_REXTERNAL,
- self.tr('For raster layers, use r.external (faster) instead of r.in.gdal'),
- False))
- ProcessingConfig.addSetting(Setting(
- self.name(),
- GrassUtils.GRASS_USE_VEXTERNAL,
- self.tr('For vector layers, use v.external (faster) instead of v.in.ogr'),
- False))
+ ProcessingConfig.addSetting(
+ Setting(
+ self.name(),
+ GrassUtils.GRASS_USE_REXTERNAL,
+ self.tr(
+ "For raster layers, use r.external (faster) instead of r.in.gdal"
+ ),
+ False,
+ )
+ )
+ ProcessingConfig.addSetting(
+ Setting(
+ self.name(),
+ GrassUtils.GRASS_USE_VEXTERNAL,
+ self.tr(
+ "For vector layers, use v.external (faster) instead of v.in.ogr"
+ ),
+ False,
+ )
+ )
ProcessingConfig.readSettings()
self.refreshAlgorithms()
@@ -81,68 +105,94 @@ def unload(self):
ProcessingConfig.removeSetting(GrassUtils.GRASS_USE_REXTERNAL)
ProcessingConfig.removeSetting(GrassUtils.GRASS_USE_VEXTERNAL)
- def parse_algorithms(self) -> List[QgsProcessingAlgorithm]:
+ def parse_algorithms(self) -> list[QgsProcessingAlgorithm]:
"""
Parses all algorithm sources and returns a list of all GRASS
algorithms.
"""
algs = []
for folder in GrassUtils.grassDescriptionFolders():
- if (folder / 'algorithms.json').exists():
+ if (folder / "algorithms.json").exists():
# fast approach -- use aggregated JSON summary of algorithms
- with open(folder / 'algorithms.json', 'rt', encoding='utf8') as f_in:
+ with open(folder / "algorithms.json", encoding="utf8") as f_in:
algorithm_strings = f_in.read()
algorithms_json = json.loads(algorithm_strings)
for algorithm_json in algorithms_json:
try:
alg = GrassAlgorithm(
- json_definition=algorithm_json,
- description_folder=folder)
- if alg.name().strip() != '':
+ json_definition=algorithm_json, description_folder=folder
+ )
+ if alg.name().strip() != "":
algs.append(alg)
else:
- QgsMessageLog.logMessage(self.tr('Could not open GRASS GIS algorithm: {0}').format(algorithm_json.get('name')), self.tr('Processing'), Qgis.MessageLevel.Critical)
+ QgsMessageLog.logMessage(
+ self.tr(
+ "Could not open GRASS GIS algorithm: {0}"
+ ).format(algorithm_json.get("name")),
+ self.tr("Processing"),
+ Qgis.MessageLevel.Critical,
+ )
except Exception as e:
QgsMessageLog.logMessage(
- self.tr('Could not open GRASS GIS algorithm: {0}\n{1}').format(algorithm_json.get('name'), e), self.tr('Processing'), Qgis.MessageLevel.Critical)
+ self.tr(
+ "Could not open GRASS GIS algorithm: {0}\n{1}"
+ ).format(algorithm_json.get("name"), e),
+ self.tr("Processing"),
+ Qgis.MessageLevel.Critical,
+ )
else:
# slow approach - pass txt files one by one
- for descriptionFile in folder.glob('*.txt'):
+ for descriptionFile in folder.glob("*.txt"):
try:
- alg = GrassAlgorithm(
- description_file=descriptionFile)
- if alg.name().strip() != '':
+ alg = GrassAlgorithm(description_file=descriptionFile)
+ if alg.name().strip() != "":
algs.append(alg)
else:
- QgsMessageLog.logMessage(self.tr('Could not open GRASS GIS algorithm: {0}').format(descriptionFile), self.tr('Processing'), Qgis.MessageLevel.Critical)
+ QgsMessageLog.logMessage(
+ self.tr(
+ "Could not open GRASS GIS algorithm: {0}"
+ ).format(descriptionFile),
+ self.tr("Processing"),
+ Qgis.MessageLevel.Critical,
+ )
except Exception as e:
QgsMessageLog.logMessage(
- self.tr('Could not open GRASS GIS algorithm: {0}\n{1}').format(descriptionFile, e), self.tr('Processing'), Qgis.MessageLevel.Critical)
+ self.tr(
+ "Could not open GRASS GIS algorithm: {0}\n{1}"
+ ).format(descriptionFile, e),
+ self.tr("Processing"),
+ Qgis.MessageLevel.Critical,
+ )
return algs
def loadAlgorithms(self):
version = GrassUtils.installedVersion(True)
if version is None:
- QgsMessageLog.logMessage(self.tr('Problem with GRASS installation: GRASS was not found or is not correctly installed'),
- self.tr('Processing'), Qgis.MessageLevel.Critical)
+ QgsMessageLog.logMessage(
+ self.tr(
+ "Problem with GRASS installation: GRASS was not found or is not correctly installed"
+ ),
+ self.tr("Processing"),
+ Qgis.MessageLevel.Critical,
+ )
return
for a in self.parse_algorithms():
self.addAlgorithm(a)
def name(self):
- return 'GRASS'
+ return "GRASS"
def longName(self):
version = GrassUtils.installedVersion()
- return 'GRASS GIS ({})'.format(version) if version is not None else "GRASS GIS"
+ return f"GRASS GIS ({version})" if version is not None else "GRASS GIS"
def id(self):
- return 'grass'
+ return "grass"
def helpId(self):
- return 'grass7'
+ return "grass7"
def icon(self):
return QgsApplication.getThemeIcon("/providerGrass.svg")
@@ -172,7 +222,7 @@ def supportedOutputRasterLayerExtensions(self):
def canBeActivated(self):
return not bool(GrassUtils.checkGrassIsInstalled())
- def tr(self, string, context=''):
- if context == '':
- context = 'Grass7AlgorithmProvider'
+ def tr(self, string, context=""):
+ if context == "":
+ context = "Grass7AlgorithmProvider"
return QCoreApplication.translate(context, string)
diff --git a/python/plugins/grassprovider/grass_utils.py b/python/plugins/grassprovider/grass_utils.py
index 37c784c09d7d..0818efed030d 100644
--- a/python/plugins/grassprovider/grass_utils.py
+++ b/python/plugins/grassprovider/grass_utils.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Victor Olaya'
-__date__ = 'February 2015'
-__copyright__ = '(C) 2014-2015, Victor Olaya'
+__author__ = "Victor Olaya"
+__date__ = "February 2015"
+__copyright__ = "(C) 2014-2015, Victor Olaya"
import os
import re
@@ -25,16 +25,9 @@
import stat
import subprocess
import sys
-from dataclasses import (
- dataclass,
- field
-)
+from dataclasses import dataclass, field
from pathlib import Path
-from typing import (
- Optional,
- List,
- Dict
-)
+from typing import Optional, List, Dict
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
@@ -52,22 +45,22 @@
class GrassUtils:
- GRASS_REGION_XMIN = 'GRASS7_REGION_XMIN'
- GRASS_REGION_YMIN = 'GRASS7_REGION_YMIN'
- GRASS_REGION_XMAX = 'GRASS7_REGION_XMAX'
- GRASS_REGION_YMAX = 'GRASS7_REGION_YMAX'
- GRASS_REGION_CELLSIZE = 'GRASS7_REGION_CELLSIZE'
- GRASS_LOG_COMMANDS = 'GRASS7_LOG_COMMANDS'
- GRASS_LOG_CONSOLE = 'GRASS7_LOG_CONSOLE'
- GRASS_HELP_URL = 'GRASS_HELP_URL'
- GRASS_USE_REXTERNAL = 'GRASS_USE_REXTERNAL'
- GRASS_USE_VEXTERNAL = 'GRASS_USE_VEXTERNAL'
+ GRASS_REGION_XMIN = "GRASS7_REGION_XMIN"
+ GRASS_REGION_YMIN = "GRASS7_REGION_YMIN"
+ GRASS_REGION_XMAX = "GRASS7_REGION_XMAX"
+ GRASS_REGION_YMAX = "GRASS7_REGION_YMAX"
+ GRASS_REGION_CELLSIZE = "GRASS7_REGION_CELLSIZE"
+ GRASS_LOG_COMMANDS = "GRASS7_LOG_COMMANDS"
+ GRASS_LOG_CONSOLE = "GRASS7_LOG_CONSOLE"
+ GRASS_HELP_URL = "GRASS_HELP_URL"
+ GRASS_USE_REXTERNAL = "GRASS_USE_REXTERNAL"
+ GRASS_USE_VEXTERNAL = "GRASS_USE_VEXTERNAL"
# TODO Review all default options formats
GRASS_RASTER_FORMATS_CREATEOPTS = {
- 'GTiff': 'TFW=YES,COMPRESS=LZW',
- 'PNG': 'ZLEVEL=9',
- 'WEBP': 'QUALITY=85'
+ "GTiff": "TFW=YES,COMPRESS=LZW",
+ "PNG": "ZLEVEL=9",
+ "WEBP": "QUALITY=85",
}
sessionRunning = False
@@ -89,9 +82,9 @@ def grassBatchJobFilename():
"""
gisdbase = GrassUtils.grassDataFolder()
if isWindows():
- batchFile = os.path.join(gisdbase, 'grass_batch_job.cmd')
+ batchFile = os.path.join(gisdbase, "grass_batch_job.cmd")
else:
- batchFile = os.path.join(gisdbase, 'grass_batch_job.sh')
+ batchFile = os.path.join(gisdbase, "grass_batch_job.sh")
return batchFile
@staticmethod
@@ -101,8 +94,8 @@ def exportCrsWktToFile(crs, context: QgsProcessingContext):
to this file
"""
wkt = crs.toWkt(QgsCoordinateReferenceSystem.WktVariant.WKT_PREFERRED)
- wkt_file = QgsProcessingUtils.generateTempFilename('crs.prj', context)
- with open(wkt_file, 'w', encoding='utf-8') as f:
+ wkt_file = QgsProcessingUtils.generateTempFilename("crs.prj", context)
+ with open(wkt_file, "w", encoding="utf-8") as f:
f.write(wkt)
return wkt_file
@@ -125,13 +118,13 @@ def installedVersion(run=False):
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
si.wShowWindow = subprocess.SW_HIDE
with subprocess.Popen(
- [GrassUtils.command, '-v'],
+ [GrassUtils.command, "-v"],
shell=False,
stdout=subprocess.PIPE,
stdin=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
universal_newlines=True,
- startupinfo=si if isWindows() else None
+ startupinfo=si if isWindows() else None,
) as proc:
try:
lines = proc.stdout.readlines()
@@ -174,31 +167,39 @@ def searchFolder(folder):
vn = os.path.join(path, "etc", "VERSIONNUMBER")
if os.path.isfile(vn):
with open(vn) as f:
- major, minor, patch = f.readlines()[0].split(' ')[0].split('.')
- if patch != 'svn':
- patch = ''
+ major, minor, patch = f.readlines()[0].split(" ")[0].split(".")
+ if patch != "svn":
+ patch = ""
cmdList = [
- "grass{}{}{}".format(major, minor, patch),
+ f"grass{major}{minor}{patch}",
"grass",
- "grass{}{}{}.{}".format(major, minor, patch,
- "bat" if isWindows() else "sh"),
- "grass.{}".format("bat" if isWindows() else "sh")
+ "grass{}{}{}.{}".format(
+ major, minor, patch, "bat" if isWindows() else "sh"
+ ),
+ "grass.{}".format("bat" if isWindows() else "sh"),
]
else:
- cmdList = ["grass80", "grass78", "grass76", "grass74", "grass72",
- "grass70", "grass"]
+ cmdList = [
+ "grass80",
+ "grass78",
+ "grass76",
+ "grass74",
+ "grass72",
+ "grass70",
+ "grass",
+ ]
cmdList.extend(
- ["{}.{}".format(b, "bat" if isWindows() else "sh") for b in
- cmdList])
+ ["{}.{}".format(b, "bat" if isWindows() else "sh") for b in cmdList]
+ )
# For MS-Windows there is a difference between GRASS Path and GRASS binary
if isWindows():
# If nothing found, use OSGEO4W or QgsPrefix:
if "OSGEO4W_ROOT" in os.environ:
- testFolder = str(os.environ['OSGEO4W_ROOT'])
+ testFolder = str(os.environ["OSGEO4W_ROOT"])
else:
testFolder = str(QgsApplication.prefixPath())
- testFolder = os.path.join(testFolder, 'bin')
+ testFolder = os.path.join(testFolder, "bin")
command = searchFolder(testFolder)
elif isMac():
# Search in grassPath
@@ -214,7 +215,7 @@ def searchFolder(folder):
if command:
GrassUtils.command = command
- if path == '':
+ if path == "":
GrassUtils.path = os.path.dirname(command)
return command
@@ -229,7 +230,7 @@ def grassPath():
return GrassUtils.path
if not isWindows() and not isMac():
- return ''
+ return ""
folder = None
# Under MS-Windows, we use GISBASE or QGIS Path for folder
@@ -238,16 +239,21 @@ def grassPath():
folder = os.environ["GISBASE"]
else:
testfolder = os.path.join(
- os.path.dirname(QgsApplication.prefixPath()), 'grass')
+ os.path.dirname(QgsApplication.prefixPath()), "grass"
+ )
if os.path.isdir(testfolder):
- grassfolders = sorted([f for f in os.listdir(testfolder) if
- f.startswith(
- "grass-7.") and os.path.isdir(
- os.path.join(testfolder, f))],
- reverse=True,
- key=lambda x: [int(v) for v in x[
- len("grass-"):].split(
- '.') if v != 'svn'])
+ grassfolders = sorted(
+ [
+ f
+ for f in os.listdir(testfolder)
+ if f.startswith("grass-7.")
+ and os.path.isdir(os.path.join(testfolder, f))
+ ],
+ reverse=True,
+ key=lambda x: [
+ int(v) for v in x[len("grass-") :].split(".") if v != "svn"
+ ],
+ )
if grassfolders:
folder = os.path.join(testfolder, grassfolders[0])
elif isMac():
@@ -256,18 +262,21 @@ def grassPath():
folder = os.environ["GISBASE"]
else:
# Find grass folder if it exists inside QGIS bundle
- for version in ['', '8', '7', '80', '78', '76', '74', '72',
- '71', '70']:
- testfolder = os.path.join(str(QgsApplication.prefixPath()),
- 'grass{}'.format(version))
+ for version in ["", "8", "7", "80", "78", "76", "74", "72", "71", "70"]:
+ testfolder = os.path.join(
+ str(QgsApplication.prefixPath()), f"grass{version}"
+ )
if os.path.isdir(testfolder):
folder = testfolder
break
# If nothing found, try standalone GRASS installation
if folder is None:
- for version in ['8', '6', '4', '2', '1', '0']:
- testfolder = '/Applications/GRASS-7.{}.app/Contents/MacOS'.format(
- version)
+ for version in ["8", "6", "4", "2", "1", "0"]:
+ testfolder = (
+ "/Applications/GRASS-7.{}.app/Contents/MacOS".format(
+ version
+ )
+ )
if os.path.isdir(testfolder):
folder = testfolder
break
@@ -275,7 +284,7 @@ def grassPath():
if folder is not None:
GrassUtils.path = folder
- return folder or ''
+ return folder or ""
@staticmethod
def userDescriptionFolder():
@@ -283,7 +292,7 @@ def userDescriptionFolder():
Creates and returns a directory for users to create additional algorithm descriptions.
Or modified versions of stock algorithm descriptions shipped with QGIS.
"""
- folder = Path(userFolder(), 'grassaddons', 'description')
+ folder = Path(userFolder(), "grassaddons", "description")
folder.mkdir(parents=True, exist_ok=True)
return folder
@@ -294,8 +303,10 @@ def grassDescriptionFolders():
Note that the provider will load from these in sequence, so we put the userDescriptionFolder first
to allow users to create modified versions of stock algorithms shipped with QGIS.
"""
- return [GrassUtils.userDescriptionFolder(),
- Path(__file__).parent.joinpath('description')]
+ return [
+ GrassUtils.userDescriptionFolder(),
+ Path(__file__).parent.joinpath("description"),
+ ]
@staticmethod
def getWindowsCodePage():
@@ -304,26 +315,26 @@ def getWindowsCodePage():
Used into GRASS exec script under MS-Windows.
"""
from ctypes import cdll
+
return str(cdll.kernel32.GetACP())
@staticmethod
def createGrassBatchJobFileFromGrassCommands(commands):
- with open(GrassUtils.grassBatchJobFilename(), 'w') as fout:
+ with open(GrassUtils.grassBatchJobFilename(), "w") as fout:
if not isWindows():
- fout.write('#!/bin/sh\n')
+ fout.write("#!/bin/sh\n")
else:
- fout.write(
- 'chcp {}>NUL\n'.format(GrassUtils.getWindowsCodePage()))
+ fout.write(f"chcp {GrassUtils.getWindowsCodePage()}>NUL\n")
for command in commands:
GrassUtils.writeCommand(fout, command)
- fout.write('exit')
+ fout.write("exit")
@staticmethod
def grassMapsetFolder():
"""
Creates and returns the GRASS temporary DB LOCATION directory.
"""
- folder = os.path.join(GrassUtils.grassDataFolder(), 'temp_location')
+ folder = os.path.join(GrassUtils.grassDataFolder(), "temp_location")
mkdir(folder)
return folder
@@ -333,7 +344,8 @@ def grassDataFolder():
Creates and returns the GRASS temporary DB directory.
"""
tempfolder = os.path.normpath(
- os.path.join(QgsProcessingUtils.tempFolder(), 'grassdata'))
+ os.path.join(QgsProcessingUtils.tempFolder(), "grassdata")
+ )
mkdir(tempfolder)
return tempfolder
@@ -348,45 +360,46 @@ def createTempMapset():
input image or vector
"""
folder = GrassUtils.grassMapsetFolder()
- mkdir(os.path.join(folder, 'PERMANENT'))
- mkdir(os.path.join(folder, 'PERMANENT', '.tmp'))
- GrassUtils.writeGrassWindow(
- os.path.join(folder, 'PERMANENT', 'DEFAULT_WIND'))
- with open(os.path.join(folder, 'PERMANENT', 'MYNAME'), 'w') as outfile:
+ mkdir(os.path.join(folder, "PERMANENT"))
+ mkdir(os.path.join(folder, "PERMANENT", ".tmp"))
+ GrassUtils.writeGrassWindow(os.path.join(folder, "PERMANENT", "DEFAULT_WIND"))
+ with open(os.path.join(folder, "PERMANENT", "MYNAME"), "w") as outfile:
outfile.write(
- 'QGIS GRASS GIS interface: temporary data processing location.\n')
+ "QGIS GRASS GIS interface: temporary data processing location.\n"
+ )
- GrassUtils.writeGrassWindow(os.path.join(folder, 'PERMANENT', 'WIND'))
- mkdir(os.path.join(folder, 'PERMANENT', 'sqlite'))
- with open(os.path.join(folder, 'PERMANENT', 'VAR'), 'w') as outfile:
- outfile.write('DB_DRIVER: sqlite\n')
+ GrassUtils.writeGrassWindow(os.path.join(folder, "PERMANENT", "WIND"))
+ mkdir(os.path.join(folder, "PERMANENT", "sqlite"))
+ with open(os.path.join(folder, "PERMANENT", "VAR"), "w") as outfile:
+ outfile.write("DB_DRIVER: sqlite\n")
outfile.write(
- 'DB_DATABASE: $GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db\n')
+ "DB_DATABASE: $GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db\n"
+ )
@staticmethod
def writeGrassWindow(filename):
"""
Creates the GRASS Window file
"""
- with open(filename, 'w') as out:
- out.write('proj: 0\n')
- out.write('zone: 0\n')
- out.write('north: 1\n')
- out.write('south: 0\n')
- out.write('east: 1\n')
- out.write('west: 0\n')
- out.write('cols: 1\n')
- out.write('rows: 1\n')
- out.write('e-w resol: 1\n')
- out.write('n-s resol: 1\n')
- out.write('top: 1\n')
- out.write('bottom: 0\n')
- out.write('cols3: 1\n')
- out.write('rows3: 1\n')
- out.write('depths: 1\n')
- out.write('e-w resol3: 1\n')
- out.write('n-s resol3: 1\n')
- out.write('t-b resol: 1\n')
+ with open(filename, "w") as out:
+ out.write("proj: 0\n")
+ out.write("zone: 0\n")
+ out.write("north: 1\n")
+ out.write("south: 0\n")
+ out.write("east: 1\n")
+ out.write("west: 0\n")
+ out.write("cols: 1\n")
+ out.write("rows: 1\n")
+ out.write("e-w resol: 1\n")
+ out.write("n-s resol: 1\n")
+ out.write("top: 1\n")
+ out.write("bottom: 0\n")
+ out.write("cols3: 1\n")
+ out.write("rows3: 1\n")
+ out.write("depths: 1\n")
+ out.write("e-w resol3: 1\n")
+ out.write("n-s resol3: 1\n")
+ out.write("t-b resol: 1\n")
@staticmethod
def prepareGrassExecution(commands):
@@ -398,21 +411,26 @@ def prepareGrassExecution(commands):
GrassUtils.grassBin()
env = os.environ.copy()
- env['GRASS_MESSAGE_FORMAT'] = 'plain'
- if 'GISBASE' in env:
- del env['GISBASE']
+ env["GRASS_MESSAGE_FORMAT"] = "plain"
+ if "GISBASE" in env:
+ del env["GISBASE"]
GrassUtils.createGrassBatchJobFileFromGrassCommands(commands)
- os.chmod(GrassUtils.grassBatchJobFilename(),
- stat.S_IEXEC | stat.S_IREAD | stat.S_IWRITE)
- command = [GrassUtils.command,
- os.path.join(GrassUtils.grassMapsetFolder(), 'PERMANENT'),
- '--exec', GrassUtils.grassBatchJobFilename()]
+ os.chmod(
+ GrassUtils.grassBatchJobFilename(),
+ stat.S_IEXEC | stat.S_IREAD | stat.S_IWRITE,
+ )
+ command = [
+ GrassUtils.command,
+ os.path.join(GrassUtils.grassMapsetFolder(), "PERMANENT"),
+ "--exec",
+ GrassUtils.grassBatchJobFilename(),
+ ]
return command, env
@staticmethod
def executeGrass(commands, feedback, outputCommands=None):
- loglines = [GrassUtils.tr('GRASS GIS execution console output')]
+ loglines = [GrassUtils.tr("GRASS GIS execution console output")]
grassOutDone = False
command, grassenv = GrassUtils.prepareGrassExecution(commands)
# QgsMessageLog.logMessage('exec: {}'.format(command), 'DEBUG', Qgis.Info)
@@ -423,10 +441,8 @@ def executeGrass(commands, feedback, outputCommands=None):
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
si.wShowWindow = subprocess.SW_HIDE
- kw['startupinfo'] = si
- if sys.version_info >= (3, 6):
- kw['encoding'] = "cp{}".format(
- GrassUtils.getWindowsCodePage())
+ kw["startupinfo"] = si
+ kw["encoding"] = f"cp{GrassUtils.getWindowsCodePage()}"
def readline_with_recover(stdout):
"""A method wrapping stdout.readline() with try-except recovering.
@@ -440,7 +456,7 @@ def readline_with_recover(stdout):
try:
return stdout.readline()
except UnicodeDecodeError:
- return '' # replaced-text
+ return "" # replaced-text
with subprocess.Popen(
command,
@@ -450,35 +466,45 @@ def readline_with_recover(stdout):
stderr=subprocess.STDOUT,
universal_newlines=True,
env=grassenv,
- **kw
+ **kw,
) as proc:
- for line in iter(lambda: readline_with_recover(proc.stdout), ''):
- if 'GRASS_INFO_PERCENT' in line:
+ for line in iter(lambda: readline_with_recover(proc.stdout), ""):
+ if "GRASS_INFO_PERCENT" in line:
try:
- feedback.setProgress(
- int(line[len('GRASS_INFO_PERCENT') + 2:]))
+ feedback.setProgress(int(line[len("GRASS_INFO_PERCENT") + 2 :]))
except Exception:
pass
else:
- if 'r.out' in line or 'v.out' in line:
+ if "r.out" in line or "v.out" in line:
grassOutDone = True
loglines.append(line)
- if any([l in line for l in ['WARNING', GrassUtils.tr('WARNING')]]):
+ if any([l in line for l in ["WARNING", GrassUtils.tr("WARNING")]]):
feedback.pushWarning(line.strip())
- elif any([l in line for l in ['ERROR', GrassUtils.tr('ERROR')]]):
+ elif any([l in line for l in ["ERROR", GrassUtils.tr("ERROR")]]):
feedback.reportError(line.strip())
- elif 'Segmentation fault' in line:
+ elif "Segmentation fault" in line:
feedback.reportError(line.strip())
- feedback.reportError('\n' + GrassUtils.tr(
- 'GRASS command crashed :( Try a different set of input parameters and consult the GRASS algorithm manual for more information.') + '\n')
- if ProcessingConfig.getSetting(
- GrassUtils.GRASS_USE_REXTERNAL):
- feedback.reportError(GrassUtils.tr(
- 'Suggest disabling the experimental "use r.external" option from the Processing GRASS Provider options.') + '\n')
- if ProcessingConfig.getSetting(
- GrassUtils.GRASS_USE_VEXTERNAL):
- feedback.reportError(GrassUtils.tr(
- 'Suggest disabling the experimental "use v.external" option from the Processing GRASS Provider options.') + '\n')
+ feedback.reportError(
+ "\n"
+ + GrassUtils.tr(
+ "GRASS command crashed :( Try a different set of input parameters and consult the GRASS algorithm manual for more information."
+ )
+ + "\n"
+ )
+ if ProcessingConfig.getSetting(GrassUtils.GRASS_USE_REXTERNAL):
+ feedback.reportError(
+ GrassUtils.tr(
+ 'Suggest disabling the experimental "use r.external" option from the Processing GRASS Provider options.'
+ )
+ + "\n"
+ )
+ if ProcessingConfig.getSetting(GrassUtils.GRASS_USE_VEXTERNAL):
+ feedback.reportError(
+ GrassUtils.tr(
+ 'Suggest disabling the experimental "use v.external" option from the Processing GRASS Provider options.'
+ )
+ + "\n"
+ )
elif line.strip():
feedback.pushConsoleInfo(line.strip())
@@ -488,18 +514,15 @@ def readline_with_recover(stdout):
# are usually the output ones. If that is the case runs the output
# commands again.
if not grassOutDone and outputCommands:
- command, grassenv = GrassUtils.prepareGrassExecution(
- outputCommands)
+ command, grassenv = GrassUtils.prepareGrassExecution(outputCommands)
# For MS-Windows, we need to hide the console window.
kw = {}
if isWindows():
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
si.wShowWindow = subprocess.SW_HIDE
- kw['startupinfo'] = si
- if sys.version_info >= (3, 6):
- kw['encoding'] = "cp{}".format(
- GrassUtils.getWindowsCodePage())
+ kw["startupinfo"] = si
+ kw["encoding"] = f"cp{GrassUtils.getWindowsCodePage()}"
with subprocess.Popen(
command,
shell=False,
@@ -508,17 +531,17 @@ def readline_with_recover(stdout):
stderr=subprocess.STDOUT,
universal_newlines=True,
env=grassenv,
- **kw
+ **kw,
) as proc:
- for line in iter(lambda: readline_with_recover(proc.stdout),
- ''):
- if 'GRASS_INFO_PERCENT' in line:
+ for line in iter(lambda: readline_with_recover(proc.stdout), ""):
+ if "GRASS_INFO_PERCENT" in line:
try:
- feedback.setProgress(int(
- line[len('GRASS_INFO_PERCENT') + 2:]))
+ feedback.setProgress(
+ int(line[len("GRASS_INFO_PERCENT") + 2 :])
+ )
except Exception:
pass
- if any(l in line for l in ['WARNING', 'ERROR']):
+ if any(l in line for l in ["WARNING", "ERROR"]):
loglines.append(line.strip())
feedback.reportError(line.strip())
elif line.strip():
@@ -526,8 +549,9 @@ def readline_with_recover(stdout):
feedback.pushConsoleInfo(line.strip())
if ProcessingConfig.getSetting(GrassUtils.GRASS_LOG_CONSOLE):
- QgsMessageLog.logMessage('\n'.join(loglines), 'Processing',
- Qgis.MessageLevel.Info)
+ QgsMessageLog.logMessage(
+ "\n".join(loglines), "Processing", Qgis.MessageLevel.Info
+ )
# GRASS session is used to hold the layers already exported or
# produced in GRASS between multiple calls to GRASS algorithms.
@@ -557,8 +581,8 @@ def getSessionLayers():
@staticmethod
def addSessionLayers(exportedLayers):
GrassUtils.sessionLayers = dict(
- list(GrassUtils.sessionLayers.items()) +
- list(exportedLayers.items()))
+ list(GrassUtils.sessionLayers.items()) + list(exportedLayers.items())
+ )
@staticmethod
def checkGrassIsInstalled(ignorePreviousState=False):
@@ -570,14 +594,15 @@ def checkGrassIsInstalled(ignorePreviousState=False):
if GrassUtils.installedVersion() is not None:
# For Ms-Windows, we check GRASS binaries
if isWindows():
- cmdpath = os.path.join(GrassUtils.path, 'bin',
- 'r.out.gdal.exe')
+ cmdpath = os.path.join(GrassUtils.path, "bin", "r.out.gdal.exe")
if not os.path.exists(cmdpath):
return GrassUtils.tr(
'The GRASS GIS folder "{}" does not contain a valid set '
- 'of GRASS modules.\nPlease, check that GRASS is correctly '
- 'installed and available on your system.'.format(
- os.path.join(GrassUtils.path, 'bin')))
+ "of GRASS modules.\nPlease, check that GRASS is correctly "
+ "installed and available on your system.".format(
+ os.path.join(GrassUtils.path, "bin")
+ )
+ )
GrassUtils.isGrassInstalled = True
return
# Return error messages
@@ -586,35 +611,39 @@ def checkGrassIsInstalled(ignorePreviousState=False):
if isWindows() or isMac():
if GrassUtils.path is None:
return GrassUtils.tr(
- 'Could not locate GRASS GIS folder. Please make '
- 'sure that GRASS GIS is correctly installed before '
- 'running GRASS algorithms.')
+ "Could not locate GRASS GIS folder. Please make "
+ "sure that GRASS GIS is correctly installed before "
+ "running GRASS algorithms."
+ )
if GrassUtils.command is None:
return GrassUtils.tr(
- 'GRASS GIS binary {} can\'t be found on this system from a shell. '
- 'Please install it or configure your PATH {} environment variable.'.format(
- '(grass.bat)' if isWindows() else '(grass.sh)',
- 'or OSGEO4W_ROOT' if isWindows() else ''))
+ "GRASS GIS binary {} can't be found on this system from a shell. "
+ "Please install it or configure your PATH {} environment variable.".format(
+ "(grass.bat)" if isWindows() else "(grass.sh)",
+ "or OSGEO4W_ROOT" if isWindows() else "",
+ )
+ )
# GNU/Linux
else:
return GrassUtils.tr(
- 'GRASS can\'t be found on this system from a shell. '
- 'Please install it or configure your PATH environment variable.')
+ "GRASS can't be found on this system from a shell. "
+ "Please install it or configure your PATH environment variable."
+ )
@staticmethod
- def tr(string, context=''):
- if context == '':
- context = 'Grass7Utils'
+ def tr(string, context=""):
+ if context == "":
+ context = "Grass7Utils"
return QCoreApplication.translate(context, string)
@staticmethod
def writeCommand(output, command):
try:
# Python 2
- output.write(command.encode('utf8') + '\n')
+ output.write(command.encode("utf8") + "\n")
except TypeError:
# Python 3
- output.write(command + '\n')
+ output.write(command + "\n")
@staticmethod
def grassHelpPath():
@@ -623,13 +652,15 @@ def grassHelpPath():
if not helpPath:
if isWindows() or isMac():
if GrassUtils.path is not None:
- localPath = os.path.join(GrassUtils.path, 'docs/html')
+ localPath = os.path.join(GrassUtils.path, "docs/html")
if os.path.exists(localPath):
helpPath = os.path.abspath(localPath)
else:
- searchPaths = ['/usr/share/doc/grass-doc/html',
- '/opt/grass/docs/html',
- '/usr/share/doc/grass/docs/html']
+ searchPaths = [
+ "/usr/share/doc/grass-doc/html",
+ "/opt/grass/docs/html",
+ "/usr/share/doc/grass/docs/html",
+ ]
for path in searchPaths:
if os.path.exists(path):
helpPath = os.path.abspath(path)
@@ -638,11 +669,11 @@ def grassHelpPath():
if helpPath:
return helpPath
elif GrassUtils.version:
- version = GrassUtils.version.replace('.', '')[:2]
- return 'https://grass.osgeo.org/grass{}/manuals/'.format(version)
+ version = GrassUtils.version.replace(".", "")[:2]
+ return f"https://grass.osgeo.org/grass{version}/manuals/"
else:
# GRASS not available!
- return 'https://grass.osgeo.org/grass-stable/manuals/'
+ return "https://grass.osgeo.org/grass-stable/manuals/"
@staticmethod
def getSupportedOutputRasterExtensions():
@@ -660,11 +691,11 @@ def getRasterFormatFromFilename(filename):
:return: The Gdal short format name for extension.
"""
ext = os.path.splitext(filename)[1].lower()
- ext = ext.lstrip('.')
+ ext = ext.lstrip(".")
if ext:
supported = GdalUtils.getSupportedRasters()
for name in list(supported.keys()):
exts = supported[name]
if ext in exts:
return name
- return 'GTiff'
+ return "GTiff"
diff --git a/python/plugins/grassprovider/parsed_description.py b/python/plugins/grassprovider/parsed_description.py
index 0b031936317e..675860eb0178 100644
--- a/python/plugins/grassprovider/parsed_description.py
+++ b/python/plugins/grassprovider/parsed_description.py
@@ -10,16 +10,9 @@
"""
import re
-from dataclasses import (
- dataclass,
- field
-)
+from dataclasses import dataclass, field
from pathlib import Path
-from typing import (
- Optional,
- List,
- Dict
-)
+from typing import Optional, List, Dict
@dataclass
@@ -28,7 +21,7 @@ class ParsedDescription:
Results of parsing a description file
"""
- GROUP_ID_REGEX = re.compile(r'^[^\s(]+')
+ GROUP_ID_REGEX = re.compile(r"^[^\s(]+")
grass_command: Optional[str] = None
short_description: Optional[str] = None
@@ -39,28 +32,28 @@ class ParsedDescription:
ext_path: Optional[str] = None
- hardcoded_strings: List[str] = field(default_factory=list)
- param_strings: List[str] = field(default_factory=list)
+ hardcoded_strings: list[str] = field(default_factory=list)
+ param_strings: list[str] = field(default_factory=list)
- def as_dict(self) -> Dict:
+ def as_dict(self) -> dict:
"""
Returns a JSON serializable dictionary representing the parsed
description
"""
return {
- 'name': self.name,
- 'display_name': self.display_name,
- 'command': self.grass_command,
- 'short_description': self.short_description,
- 'group': self.group,
- 'group_id': self.group_id,
- 'ext_path': self.ext_path,
- 'hardcoded_strings': self.hardcoded_strings,
- 'parameters': self.param_strings
+ "name": self.name,
+ "display_name": self.display_name,
+ "command": self.grass_command,
+ "short_description": self.short_description,
+ "group": self.group,
+ "group_id": self.group_id,
+ "ext_path": self.ext_path,
+ "hardcoded_strings": self.hardcoded_strings,
+ "parameters": self.param_strings,
}
@staticmethod
- def from_dict(description: Dict) -> 'ParsedDescription':
+ def from_dict(description: dict) -> "ParsedDescription":
"""
Parses a dictionary as a description and returns the result
"""
@@ -68,26 +61,26 @@ def from_dict(description: Dict) -> 'ParsedDescription':
from qgis.PyQt.QtCore import QCoreApplication
result = ParsedDescription()
- result.name = description.get('name')
- result.display_name = description.get('display_name')
- result.grass_command = description.get('command')
+ result.name = description.get("name")
+ result.display_name = description.get("display_name")
+ result.grass_command = description.get("command")
result.short_description = QCoreApplication.translate(
- "GrassAlgorithm",
- description.get('short_description')
+ "GrassAlgorithm", description.get("short_description")
)
- result.group = QCoreApplication.translate("GrassAlgorithm",
- description.get('group'))
- result.group_id = description.get('group_id')
- result.ext_path = description.get('ext_path')
- result.hardcoded_strings = description.get('hardcoded_strings', [])
- result.param_strings = description.get('parameters', [])
+ result.group = QCoreApplication.translate(
+ "GrassAlgorithm", description.get("group")
+ )
+ result.group_id = description.get("group_id")
+ result.ext_path = description.get("ext_path")
+ result.hardcoded_strings = description.get("hardcoded_strings", [])
+ result.param_strings = description.get("parameters", [])
return result
@staticmethod
def parse_description_file(
- description_file: Path,
- translate: bool = True) -> 'ParsedDescription':
+ description_file: Path, translate: bool = True
+ ) -> "ParsedDescription":
"""
Parses a description file and returns the result
"""
@@ -96,42 +89,44 @@ def parse_description_file(
with description_file.open() as lines:
# First line of the file is the Grass algorithm name
- line = lines.readline().strip('\n').strip()
+ line = lines.readline().strip("\n").strip()
result.grass_command = line
# Second line if the algorithm name in Processing
- line = lines.readline().strip('\n').strip()
+ line = lines.readline().strip("\n").strip()
result.short_description = line
if " - " not in line:
result.name = result.grass_command
else:
- result.name = line[:line.find(' ')].lower()
+ result.name = line[: line.find(" ")].lower()
if translate:
from qgis.PyQt.QtCore import QCoreApplication
+
result.short_description = QCoreApplication.translate(
- "GrassAlgorithm", line)
+ "GrassAlgorithm", line
+ )
else:
result.short_description = line
result.display_name = result.name
# Read the grass group
- line = lines.readline().strip('\n').strip()
+ line = lines.readline().strip("\n").strip()
if translate:
from qgis.PyQt.QtCore import QCoreApplication
- result.group = QCoreApplication.translate("GrassAlgorithm",
- line)
+
+ result.group = QCoreApplication.translate("GrassAlgorithm", line)
else:
result.group = line
- result.group_id = ParsedDescription.GROUP_ID_REGEX.search(
- line).group(0).lower()
+ result.group_id = (
+ ParsedDescription.GROUP_ID_REGEX.search(line).group(0).lower()
+ )
# Then you have parameters/output definition
- line = lines.readline().strip('\n').strip()
- while line != '':
- line = line.strip('\n').strip()
- if line.startswith('Hardcoded'):
- result.hardcoded_strings.append(
- line[len('Hardcoded|'):])
+ line = lines.readline().strip("\n").strip()
+ while line != "":
+ line = line.strip("\n").strip()
+ if line.startswith("Hardcoded"):
+ result.hardcoded_strings.append(line[len("Hardcoded|") :])
result.param_strings.append(line)
- line = lines.readline().strip('\n').strip()
+ line = lines.readline().strip("\n").strip()
return result
diff --git a/python/plugins/grassprovider/tests/AlgorithmsTestBase.py b/python/plugins/grassprovider/tests/AlgorithmsTestBase.py
index 9076368fedf2..353b44384623 100644
--- a/python/plugins/grassprovider/tests/AlgorithmsTestBase.py
+++ b/python/plugins/grassprovider/tests/AlgorithmsTestBase.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Matthias Kuhn'
-__date__ = 'January 2016'
-__copyright__ = '(C) 2016, Matthias Kuhn'
+__author__ = "Matthias Kuhn"
+__date__ = "January 2016"
+__copyright__ = "(C) 2016, Matthias Kuhn"
import os
@@ -34,20 +34,20 @@
from numpy import nan_to_num
from copy import deepcopy
-from qgis.core import (QgsVectorLayer,
- QgsRasterLayer,
- QgsCoordinateReferenceSystem,
- QgsFeatureRequest,
- QgsMapLayer,
- QgsProject,
- QgsApplication,
- QgsProcessingContext,
- QgsProcessingUtils,
- QgsProcessingFeedback)
-from qgis.analysis import (QgsNativeAlgorithms)
-from qgis.testing import (_UnexpectedSuccess,
- QgisTestCase,
- start_app)
+from qgis.core import (
+ QgsVectorLayer,
+ QgsRasterLayer,
+ QgsCoordinateReferenceSystem,
+ QgsFeatureRequest,
+ QgsMapLayer,
+ QgsProject,
+ QgsApplication,
+ QgsProcessingContext,
+ QgsProcessingUtils,
+ QgsProcessingFeedback,
+)
+from qgis.analysis import QgsNativeAlgorithms
+from qgis.testing import _UnexpectedSuccess, QgisTestCase, start_app
from utilities import unitTestDataPath
@@ -57,7 +57,7 @@
def processingTestDataPath():
- return os.path.join(os.path.dirname(__file__), 'testdata')
+ return os.path.join(os.path.dirname(__file__), "testdata")
class AlgorithmsTest:
@@ -66,13 +66,19 @@ def test_algorithms(self):
"""
This is the main test function. All others will be executed based on the definitions in testdata/algorithm_tests.yaml
"""
- with open(os.path.join(processingTestDataPath(), self.definition_file())) as stream:
+ with open(
+ os.path.join(processingTestDataPath(), self.definition_file())
+ ) as stream:
algorithm_tests = yaml.load(stream, Loader=yaml.SafeLoader)
- if 'tests' in algorithm_tests and algorithm_tests['tests'] is not None:
- for idx, algtest in enumerate(algorithm_tests['tests']):
- print('About to start {} of {}: "{}"'.format(idx, len(algorithm_tests['tests']), algtest['name']))
- yield self.check_algorithm, algtest['name'], algtest
+ if "tests" in algorithm_tests and algorithm_tests["tests"] is not None:
+ for idx, algtest in enumerate(algorithm_tests["tests"]):
+ print(
+ 'About to start {} of {}: "{}"'.format(
+ idx, len(algorithm_tests["tests"]), algtest["name"]
+ )
+ )
+ yield self.check_algorithm, algtest["name"], algtest
def check_algorithm(self, name, defs):
"""
@@ -83,25 +89,29 @@ def check_algorithm(self, name, defs):
self.vector_layer_params = {}
QgsProject.instance().clear()
- if 'project' in defs:
- full_project_path = os.path.join(processingTestDataPath(), defs['project'])
+ if "project" in defs:
+ full_project_path = os.path.join(processingTestDataPath(), defs["project"])
project_read_success = QgsProject.instance().read(full_project_path)
- self.assertTrue(project_read_success, 'Failed to load project file: ' + defs['project'])
-
- if 'project_crs' in defs:
- QgsProject.instance().setCrs(QgsCoordinateReferenceSystem(defs['project_crs']))
+ self.assertTrue(
+ project_read_success, "Failed to load project file: " + defs["project"]
+ )
+
+ if "project_crs" in defs:
+ QgsProject.instance().setCrs(
+ QgsCoordinateReferenceSystem(defs["project_crs"])
+ )
else:
QgsProject.instance().setCrs(QgsCoordinateReferenceSystem())
- if 'ellipsoid' in defs:
- QgsProject.instance().setEllipsoid(defs['ellipsoid'])
+ if "ellipsoid" in defs:
+ QgsProject.instance().setEllipsoid(defs["ellipsoid"])
else:
- QgsProject.instance().setEllipsoid('')
+ QgsProject.instance().setEllipsoid("")
- params = self.load_params(defs['params'])
+ params = self.load_params(defs["params"])
- print('Running alg: "{}"'.format(defs['algorithm']))
- alg = QgsApplication.processingRegistry().createAlgorithmById(defs['algorithm'])
+ print('Running alg: "{}"'.format(defs["algorithm"]))
+ alg = QgsApplication.processingRegistry().createAlgorithmById(defs["algorithm"])
parameters = {}
if isinstance(params, list):
@@ -111,45 +121,47 @@ def check_algorithm(self, name, defs):
for k, p in params.items():
parameters[k] = p
- for r, p in list(defs['results'].items()):
- if 'in_place_result' not in p or not p['in_place_result']:
+ for r, p in list(defs["results"].items()):
+ if "in_place_result" not in p or not p["in_place_result"]:
parameters[r] = self.load_result_param(p)
expectFailure = False
- if 'expectedFailure' in defs:
- exec(('\n'.join(defs['expectedFailure'][:-1])), globals(), locals())
- expectFailure = eval(defs['expectedFailure'][-1])
+ if "expectedFailure" in defs:
+ exec(("\n".join(defs["expectedFailure"][:-1])), globals(), locals())
+ expectFailure = eval(defs["expectedFailure"][-1])
- if 'expectedException' in defs:
+ if "expectedException" in defs:
expectFailure = True
# ignore user setting for invalid geometry handling
context = QgsProcessingContext()
context.setProject(QgsProject.instance())
- if 'skipInvalid' in defs and defs['skipInvalid']:
- context.setInvalidGeometryCheck(QgsFeatureRequest.InvalidGeometryCheck.GeometrySkipInvalid)
+ if "skipInvalid" in defs and defs["skipInvalid"]:
+ context.setInvalidGeometryCheck(
+ QgsFeatureRequest.InvalidGeometryCheck.GeometrySkipInvalid
+ )
feedback = QgsProcessingFeedback()
- print('Algorithm parameters are {}'.format(parameters))
+ print(f"Algorithm parameters are {parameters}")
# first check that algorithm accepts the parameters we pass...
ok, msg = alg.checkParameterValues(parameters, context)
- self.assertTrue(ok, 'Algorithm failed checkParameterValues with result {}'.format(msg))
+ self.assertTrue(ok, f"Algorithm failed checkParameterValues with result {msg}")
if expectFailure:
try:
results, ok = alg.run(parameters, context, feedback)
- self.check_results(results, context, parameters, defs['results'])
+ self.check_results(results, context, parameters, defs["results"])
if ok:
raise _UnexpectedSuccess
except Exception:
pass
else:
results, ok = alg.run(parameters, context, feedback)
- self.assertTrue(ok, 'params: {}, results: {}'.format(parameters, results))
- self.check_results(results, context, parameters, defs['results'])
+ self.assertTrue(ok, f"params: {parameters}, results: {results}")
+ self.check_results(results, context, parameters, defs["results"])
def load_params(self, params):
"""
@@ -168,68 +180,75 @@ def load_param(self, param, id=None):
parameter based on its key `type` and return the appropriate parameter to pass to the algorithm.
"""
try:
- if param['type'] in ('vector', 'raster', 'table'):
+ if param["type"] in ("vector", "raster", "table"):
return self.load_layer(id, param).id()
- elif param['type'] == 'vrtlayers':
+ elif param["type"] == "vrtlayers":
vals = []
- for p in param['params']:
- p['layer'] = self.load_layer(None, {'type': 'vector', 'name': p['layer']})
+ for p in param["params"]:
+ p["layer"] = self.load_layer(
+ None, {"type": "vector", "name": p["layer"]}
+ )
vals.append(p)
return vals
- elif param['type'] == 'multi':
- return [self.load_param(p) for p in param['params']]
- elif param['type'] == 'file':
+ elif param["type"] == "multi":
+ return [self.load_param(p) for p in param["params"]]
+ elif param["type"] == "file":
return self.filepath_from_param(param)
- elif param['type'] == 'interpolation':
+ elif param["type"] == "interpolation":
prefix = processingTestDataPath()
- tmp = ''
- for r in param['name'].split('::|::'):
- v = r.split('::~::')
- tmp += '{}::~::{}::~::{}::~::{};'.format(os.path.join(prefix, v[0]),
- v[1], v[2], v[3])
+ tmp = ""
+ for r in param["name"].split("::|::"):
+ v = r.split("::~::")
+ tmp += "{}::~::{}::~::{}::~::{};".format(
+ os.path.join(prefix, v[0]), v[1], v[2], v[3]
+ )
return tmp[:-1]
except TypeError:
# No type specified, use whatever is there
return param
- raise KeyError("Unknown type '{}' specified for parameter".format(param['type']))
+ raise KeyError(
+ "Unknown type '{}' specified for parameter".format(param["type"])
+ )
def load_result_param(self, param):
"""
Loads a result parameter. Creates a temporary destination where the result should go to and returns this location
so it can be sent to the algorithm as parameter.
"""
- if param['type'] in ['vector', 'file', 'table', 'regex']:
+ if param["type"] in ["vector", "file", "table", "regex"]:
outdir = tempfile.mkdtemp()
self.cleanup_paths.append(outdir)
- if isinstance(param['name'], str):
- basename = os.path.basename(param['name'])
+ if isinstance(param["name"], str):
+ basename = os.path.basename(param["name"])
else:
- basename = os.path.basename(param['name'][0])
+ basename = os.path.basename(param["name"][0])
filepath = self.uri_path_join(outdir, basename)
return filepath
- elif param['type'] == 'rasterhash':
+ elif param["type"] == "rasterhash":
outdir = tempfile.mkdtemp()
self.cleanup_paths.append(outdir)
- basename = 'raster.tif'
+ basename = "raster.tif"
filepath = os.path.join(outdir, basename)
return filepath
- elif param['type'] == 'directory':
+ elif param["type"] == "directory":
outdir = tempfile.mkdtemp()
return outdir
- raise KeyError("Unknown type '{}' specified for parameter".format(param['type']))
+ raise KeyError(
+ "Unknown type '{}' specified for parameter".format(param["type"])
+ )
def load_layers(self, id, param):
layers = []
- if param['type'] in ('vector', 'table'):
- if isinstance(param['name'], str) or 'uri' in param:
+ if param["type"] in ("vector", "table"):
+ if isinstance(param["name"], str) or "uri" in param:
layers.append(self.load_layer(id, param))
else:
- for n in param['name']:
+ for n in param["name"]:
layer_param = deepcopy(param)
- layer_param['name'] = n
+ layer_param["name"] = n
layers.append(self.load_layer(id, layer_param))
else:
layers.append(self.load_layer(id, param))
@@ -242,37 +261,39 @@ def load_layer(self, id, param):
filepath = self.filepath_from_param(param)
- if 'in_place' in param and param['in_place']:
+ if "in_place" in param and param["in_place"]:
# check if alg modifies layer in place
tmpdir = tempfile.mkdtemp()
self.cleanup_paths.append(tmpdir)
path, file_name = os.path.split(filepath)
base, ext = os.path.splitext(file_name)
- for file in glob.glob(os.path.join(path, '{}.*'.format(base))):
+ for file in glob.glob(os.path.join(path, f"{base}.*")):
shutil.copy(os.path.join(path, file), tmpdir)
filepath = os.path.join(tmpdir, file_name)
self.in_place_layers[id] = filepath
- if param['type'] in ('vector', 'table'):
- gmlrex = r'\.gml\b'
+ if param["type"] in ("vector", "table"):
+ gmlrex = r"\.gml\b"
if re.search(gmlrex, filepath, re.IGNORECASE):
# ewwwww - we have to force SRS detection for GML files, otherwise they'll be loaded
# with no srs
- filepath += '|option:FORCE_SRS_DETECTION=YES'
+ filepath += "|option:FORCE_SRS_DETECTION=YES"
if filepath in self.vector_layer_params:
return self.vector_layer_params[filepath]
options = QgsVectorLayer.LayerOptions()
options.loadDefaultStyle = False
- lyr = QgsVectorLayer(filepath, param['name'], 'ogr', options)
+ lyr = QgsVectorLayer(filepath, param["name"], "ogr", options)
self.vector_layer_params[filepath] = lyr
- elif param['type'] == 'raster':
+ elif param["type"] == "raster":
options = QgsRasterLayer.LayerOptions()
options.loadDefaultStyle = False
- lyr = QgsRasterLayer(filepath, param['name'], 'gdal', options)
+ lyr = QgsRasterLayer(filepath, param["name"], "gdal", options)
- self.assertTrue(lyr.isValid(), 'Could not load layer "{}" from param {}'.format(filepath, param))
+ self.assertTrue(
+ lyr.isValid(), f'Could not load layer "{filepath}" from param {param}'
+ )
QgsProject.instance().addMapLayer(lyr)
return lyr
@@ -281,13 +302,13 @@ def filepath_from_param(self, param):
Creates a filepath from a param
"""
prefix = processingTestDataPath()
- if 'location' in param and param['location'] == 'qgs':
+ if "location" in param and param["location"] == "qgs":
prefix = unitTestDataPath()
- if 'uri' in param:
- path = param['uri']
+ if "uri" in param:
+ path = param["uri"]
else:
- path = param['name']
+ path = param["name"]
if not path:
return None
@@ -295,10 +316,10 @@ def filepath_from_param(self, param):
return self.uri_path_join(prefix, path)
def uri_path_join(self, prefix, filepath):
- if filepath.startswith('ogr:'):
+ if filepath.startswith("ogr:"):
if not prefix[-1] == os.path.sep:
prefix += os.path.sep
- filepath = re.sub(r"dbname='", "dbname='{}".format(prefix), filepath)
+ filepath = re.sub(r"dbname='", f"dbname='{prefix}", filepath)
else:
filepath = os.path.join(prefix, filepath)
@@ -309,88 +330,105 @@ def check_results(self, results, context, params, expected):
Checks if result produced by an algorithm matches with the expected specification.
"""
for id, expected_result in expected.items():
- if expected_result['type'] in ('vector', 'table'):
- if 'compare' in expected_result and not expected_result['compare']:
+ if expected_result["type"] in ("vector", "table"):
+ if "compare" in expected_result and not expected_result["compare"]:
# skipping the comparison, so just make sure output is valid
if isinstance(results[id], QgsMapLayer):
result_lyr = results[id]
else:
- result_lyr = QgsProcessingUtils.mapLayerFromString(results[id], context)
+ result_lyr = QgsProcessingUtils.mapLayerFromString(
+ results[id], context
+ )
self.assertTrue(result_lyr.isValid())
continue
expected_lyrs = self.load_layers(id, expected_result)
- if 'in_place_result' in expected_result:
- result_lyr = QgsProcessingUtils.mapLayerFromString(self.in_place_layers[id], context)
+ if "in_place_result" in expected_result:
+ result_lyr = QgsProcessingUtils.mapLayerFromString(
+ self.in_place_layers[id], context
+ )
self.assertTrue(result_lyr.isValid(), self.in_place_layers[id])
else:
try:
results[id]
except KeyError as e:
- raise KeyError('Expected result {} does not exist in {}'.format(str(e), list(results.keys())))
+ raise KeyError(
+ f"Expected result {str(e)} does not exist in {list(results.keys())}"
+ )
if isinstance(results[id], QgsMapLayer):
result_lyr = results[id]
else:
string = results[id]
- gmlrex = r'\.gml\b'
+ gmlrex = r"\.gml\b"
if re.search(gmlrex, string, re.IGNORECASE):
# ewwwww - we have to force SRS detection for GML files, otherwise they'll be loaded
# with no srs
- string += '|option:FORCE_SRS_DETECTION=YES'
+ string += "|option:FORCE_SRS_DETECTION=YES"
- result_lyr = QgsProcessingUtils.mapLayerFromString(string, context)
+ result_lyr = QgsProcessingUtils.mapLayerFromString(
+ string, context
+ )
self.assertTrue(result_lyr, results[id])
- compare = expected_result.get('compare', {})
- pk = expected_result.get('pk', None)
+ compare = expected_result.get("compare", {})
+ pk = expected_result.get("pk", None)
if len(expected_lyrs) == 1:
- self.assertLayersEqual(expected_lyrs[0], result_lyr, compare=compare, pk=pk)
+ self.assertLayersEqual(
+ expected_lyrs[0], result_lyr, compare=compare, pk=pk
+ )
else:
res = False
for l in expected_lyrs:
if self.checkLayersEqual(l, result_lyr, compare=compare, pk=pk):
res = True
break
- self.assertTrue(res, 'Could not find matching layer in expected results')
-
- elif 'rasterhash' == expected_result['type']:
- print("id:{} result:{}".format(id, results[id]))
- self.assertTrue(os.path.exists(results[id]), 'File does not exist: {}, {}'.format(results[id], params))
+ self.assertTrue(
+ res, "Could not find matching layer in expected results"
+ )
+
+ elif "rasterhash" == expected_result["type"]:
+ print(f"id:{id} result:{results[id]}")
+ self.assertTrue(
+ os.path.exists(results[id]),
+ f"File does not exist: {results[id]}, {params}",
+ )
dataset = gdal.Open(results[id], GA_ReadOnly)
dataArray = nan_to_num(dataset.ReadAsArray(0))
strhash = hashlib.sha224(dataArray.data).hexdigest()
- if not isinstance(expected_result['hash'], str):
- self.assertIn(strhash, expected_result['hash'])
+ if not isinstance(expected_result["hash"], str):
+ self.assertIn(strhash, expected_result["hash"])
else:
- self.assertEqual(strhash, expected_result['hash'])
- elif 'file' == expected_result['type']:
+ self.assertEqual(strhash, expected_result["hash"])
+ elif "file" == expected_result["type"]:
result_filepath = results[id]
- if isinstance(expected_result.get('name'), list):
+ if isinstance(expected_result.get("name"), list):
# test to see if any match expected
- for path in expected_result['name']:
- expected_filepath = self.filepath_from_param({'name': path})
+ for path in expected_result["name"]:
+ expected_filepath = self.filepath_from_param({"name": path})
if self.checkFilesEqual(expected_filepath, result_filepath):
break
else:
- expected_filepath = self.filepath_from_param({'name': expected_result['name'][0]})
+ expected_filepath = self.filepath_from_param(
+ {"name": expected_result["name"][0]}
+ )
else:
expected_filepath = self.filepath_from_param(expected_result)
self.assertFilesEqual(expected_filepath, result_filepath)
- elif 'directory' == expected_result['type']:
+ elif "directory" == expected_result["type"]:
expected_dirpath = self.filepath_from_param(expected_result)
result_dirpath = results[id]
self.assertDirectoriesEqual(expected_dirpath, result_dirpath)
- elif 'regex' == expected_result['type']:
+ elif "regex" == expected_result["type"]:
with open(results[id]) as file:
data = file.read()
- for rule in expected_result.get('rules', []):
+ for rule in expected_result.get("rules", []):
self.assertRegex(data, rule)
@@ -411,9 +449,9 @@ def tearDownClass(cls):
def testAlgorithmCompliance(self):
for p in QgsApplication.processingRegistry().providers():
- print('testing provider {}'.format(p.id()))
+ print(f"testing provider {p.id()}")
for a in p.algorithms():
- print('testing algorithm {}'.format(a.id()))
+ print(f"testing algorithm {a.id()}")
self.check_algorithm(a)
def check_algorithm(self, alg):
@@ -421,5 +459,5 @@ def check_algorithm(self, alg):
alg.helpUrl()
-if __name__ == '__main__':
+if __name__ == "__main__":
nose2.main()
diff --git a/python/plugins/grassprovider/tests/grass_algorithms_imagery_test.py b/python/plugins/grassprovider/tests/grass_algorithms_imagery_test.py
index 7e2baa9b6914..84bd78e4e510 100644
--- a/python/plugins/grassprovider/tests/grass_algorithms_imagery_test.py
+++ b/python/plugins/grassprovider/tests/grass_algorithms_imagery_test.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'May 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "May 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import AlgorithmsTestBase
@@ -25,10 +25,7 @@
import shutil
from qgis.core import QgsApplication
-from qgis.testing import (
- QgisTestCase,
- start_app
-)
+from qgis.testing import QgisTestCase, start_app
from grassprovider.grass_provider import GrassProvider
from grassprovider.grass_utils import GrassUtils
@@ -51,8 +48,8 @@ def tearDownClass(cls):
shutil.rmtree(path)
def definition_file(self):
- return 'grass_algorithms_imagery_tests.yaml'
+ return "grass_algorithms_imagery_tests.yaml"
-if __name__ == '__main__':
+if __name__ == "__main__":
nose2.main()
diff --git a/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt1.py b/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt1.py
index b26a79ef9522..36ef165d006f 100644
--- a/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt1.py
+++ b/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt1.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'May 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "May 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import AlgorithmsTestBase
@@ -25,11 +25,7 @@
import shutil
from qgis.core import QgsApplication
-from qgis.testing import (
- QgisTestCase,
- start_app
-
-)
+from qgis.testing import QgisTestCase, start_app
from grassprovider.grass_provider import GrassProvider
from grassprovider.grass_utils import GrassUtils
@@ -52,8 +48,8 @@ def tearDownClass(cls):
shutil.rmtree(path)
def definition_file(self):
- return 'grass_algorithms_raster_tests1.yaml'
+ return "grass_algorithms_raster_tests1.yaml"
-if __name__ == '__main__':
+if __name__ == "__main__":
nose2.main()
diff --git a/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt2.py b/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt2.py
index 9870ba662f2e..bd75ff27849e 100644
--- a/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt2.py
+++ b/python/plugins/grassprovider/tests/grass_algorithms_raster_test_pt2.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Médéric Ribreux'
-__date__ = 'May 2016'
-__copyright__ = '(C) 2016, Médéric Ribreux'
+__author__ = "Médéric Ribreux"
+__date__ = "May 2016"
+__copyright__ = "(C) 2016, Médéric Ribreux"
import AlgorithmsTestBase
@@ -26,20 +26,13 @@
import os
import tempfile
-from qgis.core import (
- QgsApplication,
- QgsProcessingContext,
- QgsProcessingFeedback
-)
-from qgis.testing import (
- QgisTestCase,
- start_app
-)
+from qgis.core import QgsApplication, QgsProcessingContext, QgsProcessingFeedback
+from qgis.testing import QgisTestCase, start_app
from grassprovider.grass_provider import GrassProvider
from grassprovider.grass_utils import GrassUtils
-testDataPath = os.path.join(os.path.dirname(__file__), 'testdata')
+testDataPath = os.path.join(os.path.dirname(__file__), "testdata")
class TestGrassAlgorithmsRasterTest(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
@@ -63,36 +56,42 @@ def tearDownClass(cls):
shutil.rmtree(path)
def definition_file(self):
- return 'grass_algorithms_raster_tests2.yaml'
+ return "grass_algorithms_raster_tests2.yaml"
def testNeighbors(self):
context = QgsProcessingContext()
- input_raster = os.path.join(testDataPath, 'custom', 'grass7', 'float_raster.tif')
+ input_raster = os.path.join(
+ testDataPath, "custom", "grass7", "float_raster.tif"
+ )
- alg = QgsApplication.processingRegistry().createAlgorithmById('grass:r.neighbors')
+ alg = QgsApplication.processingRegistry().createAlgorithmById(
+ "grass:r.neighbors"
+ )
self.assertIsNotNone(alg)
- temp_file = os.path.join(self.temp_dir, 'grass_output.tif')
+ temp_file = os.path.join(self.temp_dir, "grass_output.tif")
# Test an even integer for neighborhood size
- parameters = {'input': input_raster,
- 'selection': None,
- 'method': 0,
- 'size': 4,
- 'gauss': None,
- 'quantile': '',
- '-c': False,
- '-a': False,
- 'weight': '',
- 'output': temp_file,
- 'GRASS_REGION_PARAMETER': None,
- 'GRASS_REGION_CELLSIZE_PARAMETER': 0,
- 'GRASS_RASTER_FORMAT_OPT': '',
- 'GRASS_RASTER_FORMAT_META': ''}
+ parameters = {
+ "input": input_raster,
+ "selection": None,
+ "method": 0,
+ "size": 4,
+ "gauss": None,
+ "quantile": "",
+ "-c": False,
+ "-a": False,
+ "weight": "",
+ "output": temp_file,
+ "GRASS_REGION_PARAMETER": None,
+ "GRASS_REGION_CELLSIZE_PARAMETER": 0,
+ "GRASS_RASTER_FORMAT_OPT": "",
+ "GRASS_RASTER_FORMAT_META": "",
+ }
ok, msg = alg.checkParameterValues(parameters, context)
self.assertFalse(ok)
-if __name__ == '__main__':
+if __name__ == "__main__":
nose2.main()
diff --git a/python/plugins/grassprovider/tests/grass_algorithms_vector_test.py b/python/plugins/grassprovider/tests/grass_algorithms_vector_test.py
index d324df1e89eb..dd660f412542 100644
--- a/python/plugins/grassprovider/tests/grass_algorithms_vector_test.py
+++ b/python/plugins/grassprovider/tests/grass_algorithms_vector_test.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Nyall Dawson'
-__date__ = 'March 2018'
-__copyright__ = '(C) 2018, Nyall Dawson'
+__author__ = "Nyall Dawson"
+__date__ = "March 2018"
+__copyright__ = "(C) 2018, Nyall Dawson"
import AlgorithmsTestBase
@@ -27,24 +27,23 @@
import tempfile
import re
-from qgis.core import (QgsVectorLayer,
- QgsApplication,
- QgsFeature,
- QgsGeometry,
- QgsPointXY,
- QgsProcessingContext,
- QgsProject,
- QgsProcessingFeedback,
- QgsProcessingFeatureSourceDefinition)
-from qgis.testing import (
- QgisTestCase,
- start_app
+from qgis.core import (
+ QgsVectorLayer,
+ QgsApplication,
+ QgsFeature,
+ QgsGeometry,
+ QgsPointXY,
+ QgsProcessingContext,
+ QgsProject,
+ QgsProcessingFeedback,
+ QgsProcessingFeatureSourceDefinition,
)
+from qgis.testing import QgisTestCase, start_app
from grassprovider.grass_provider import GrassProvider
from grassprovider.grass_utils import GrassUtils
-testDataPath = os.path.join(os.path.dirname(__file__), 'testdata')
+testDataPath = os.path.join(os.path.dirname(__file__), "testdata")
class TestGrassAlgorithmsVectorTest(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
@@ -68,12 +67,15 @@ def tearDownClass(cls):
shutil.rmtree(path)
def definition_file(self):
- return 'grass_algorithms_vector_tests.yaml'
+ return "grass_algorithms_vector_tests.yaml"
def testMemoryLayerInput(self):
# create a memory layer and add to project and context
- layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
- "testmem", "memory")
+ layer = QgsVectorLayer(
+ "Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
+ "testmem",
+ "memory",
+ )
self.assertTrue(layer.isValid())
pr = layer.dataProvider()
f = QgsFeature()
@@ -88,30 +90,32 @@ def testMemoryLayerInput(self):
context = QgsProcessingContext()
context.setProject(QgsProject.instance())
- alg = QgsApplication.processingRegistry().createAlgorithmById('grass:v.buffer')
+ alg = QgsApplication.processingRegistry().createAlgorithmById("grass:v.buffer")
self.assertIsNotNone(alg)
- temp_file = os.path.join(self.temp_dir, 'grass_output.shp')
- parameters = {'input': 'testmem',
- 'cats': '',
- 'where': '',
- 'type': [0, 1, 4],
- 'distance': 1,
- 'minordistance': None,
- 'angle': 0,
- 'column': None,
- 'scale': 1,
- 'tolerance': 0.01,
- '-s': False,
- '-c': False,
- '-t': False,
- 'output': temp_file,
- 'GRASS_REGION_PARAMETER': None,
- 'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
- 'GRASS_MIN_AREA_PARAMETER': 0.0001,
- 'GRASS_OUTPUT_TYPE_PARAMETER': 0,
- 'GRASS_VECTOR_DSCO': '',
- 'GRASS_VECTOR_LCO': ''}
+ temp_file = os.path.join(self.temp_dir, "grass_output.shp")
+ parameters = {
+ "input": "testmem",
+ "cats": "",
+ "where": "",
+ "type": [0, 1, 4],
+ "distance": 1,
+ "minordistance": None,
+ "angle": 0,
+ "column": None,
+ "scale": 1,
+ "tolerance": 0.01,
+ "-s": False,
+ "-c": False,
+ "-t": False,
+ "output": temp_file,
+ "GRASS_REGION_PARAMETER": None,
+ "GRASS_SNAP_TOLERANCE_PARAMETER": -1,
+ "GRASS_MIN_AREA_PARAMETER": 0.0001,
+ "GRASS_OUTPUT_TYPE_PARAMETER": 0,
+ "GRASS_VECTOR_DSCO": "",
+ "GRASS_VECTOR_LCO": "",
+ }
feedback = QgsProcessingFeedback()
results, ok = alg.run(parameters, context, feedback)
@@ -119,7 +123,7 @@ def testMemoryLayerInput(self):
self.assertTrue(os.path.exists(temp_file))
# make sure that layer has correct features
- res = QgsVectorLayer(temp_file, 'res')
+ res = QgsVectorLayer(temp_file, "res")
self.assertTrue(res.isValid())
self.assertEqual(res.featureCount(), 2)
@@ -127,8 +131,11 @@ def testMemoryLayerInput(self):
def testFeatureSourceInput(self):
# create a memory layer and add to project and context
- layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
- "testmem", "memory")
+ layer = QgsVectorLayer(
+ "Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
+ "testmem",
+ "memory",
+ )
self.assertTrue(layer.isValid())
pr = layer.dataProvider()
f = QgsFeature()
@@ -148,29 +155,31 @@ def testFeatureSourceInput(self):
context = QgsProcessingContext()
context.setProject(QgsProject.instance())
- alg = QgsApplication.processingRegistry().createAlgorithmById('grass:v.buffer')
+ alg = QgsApplication.processingRegistry().createAlgorithmById("grass:v.buffer")
self.assertIsNotNone(alg)
- temp_file = os.path.join(self.temp_dir, 'grass_output_sel.shp')
- parameters = {'input': QgsProcessingFeatureSourceDefinition('testmem', True),
- 'cats': '',
- 'where': '',
- 'type': [0, 1, 4],
- 'distance': 1,
- 'minordistance': None,
- 'angle': 0,
- 'column': None,
- 'scale': 1,
- 'tolerance': 0.01,
- '-s': False,
- '-c': False,
- '-t': False,
- 'output': temp_file,
- 'GRASS_REGION_PARAMETER': None,
- 'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
- 'GRASS_MIN_AREA_PARAMETER': 0.0001,
- 'GRASS_OUTPUT_TYPE_PARAMETER': 0,
- 'GRASS_VECTOR_DSCO': '',
- 'GRASS_VECTOR_LCO': ''}
+ temp_file = os.path.join(self.temp_dir, "grass_output_sel.shp")
+ parameters = {
+ "input": QgsProcessingFeatureSourceDefinition("testmem", True),
+ "cats": "",
+ "where": "",
+ "type": [0, 1, 4],
+ "distance": 1,
+ "minordistance": None,
+ "angle": 0,
+ "column": None,
+ "scale": 1,
+ "tolerance": 0.01,
+ "-s": False,
+ "-c": False,
+ "-t": False,
+ "output": temp_file,
+ "GRASS_REGION_PARAMETER": None,
+ "GRASS_SNAP_TOLERANCE_PARAMETER": -1,
+ "GRASS_MIN_AREA_PARAMETER": 0.0001,
+ "GRASS_OUTPUT_TYPE_PARAMETER": 0,
+ "GRASS_VECTOR_DSCO": "",
+ "GRASS_VECTOR_LCO": "",
+ }
feedback = QgsProcessingFeedback()
results, ok = alg.run(parameters, context, feedback)
@@ -178,7 +187,7 @@ def testFeatureSourceInput(self):
self.assertTrue(os.path.exists(temp_file))
# make sure that layer has correct features
- res = QgsVectorLayer(temp_file, 'res')
+ res = QgsVectorLayer(temp_file, "res")
self.assertTrue(res.isValid())
self.assertEqual(res.featureCount(), 1)
@@ -186,8 +195,11 @@ def testFeatureSourceInput(self):
def testOutputToGeopackage(self):
# create a memory layer and add to project and context
- layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
- "testmem", "memory")
+ layer = QgsVectorLayer(
+ "Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer",
+ "testmem",
+ "memory",
+ )
self.assertTrue(layer.isValid())
pr = layer.dataProvider()
f = QgsFeature()
@@ -202,30 +214,32 @@ def testOutputToGeopackage(self):
context = QgsProcessingContext()
context.setProject(QgsProject.instance())
- alg = QgsApplication.processingRegistry().createAlgorithmById('grass:v.buffer')
+ alg = QgsApplication.processingRegistry().createAlgorithmById("grass:v.buffer")
self.assertIsNotNone(alg)
- temp_file = os.path.join(self.temp_dir, 'grass_output.gpkg')
- parameters = {'input': 'testmem',
- 'cats': '',
- 'where': '',
- 'type': [0, 1, 4],
- 'distance': 1,
- 'minordistance': None,
- 'angle': 0,
- 'column': None,
- 'scale': 1,
- 'tolerance': 0.01,
- '-s': False,
- '-c': False,
- '-t': False,
- 'output': temp_file,
- 'GRASS_REGION_PARAMETER': None,
- 'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
- 'GRASS_MIN_AREA_PARAMETER': 0.0001,
- 'GRASS_OUTPUT_TYPE_PARAMETER': 0,
- 'GRASS_VECTOR_DSCO': '',
- 'GRASS_VECTOR_LCO': ''}
+ temp_file = os.path.join(self.temp_dir, "grass_output.gpkg")
+ parameters = {
+ "input": "testmem",
+ "cats": "",
+ "where": "",
+ "type": [0, 1, 4],
+ "distance": 1,
+ "minordistance": None,
+ "angle": 0,
+ "column": None,
+ "scale": 1,
+ "tolerance": 0.01,
+ "-s": False,
+ "-c": False,
+ "-t": False,
+ "output": temp_file,
+ "GRASS_REGION_PARAMETER": None,
+ "GRASS_SNAP_TOLERANCE_PARAMETER": -1,
+ "GRASS_MIN_AREA_PARAMETER": 0.0001,
+ "GRASS_OUTPUT_TYPE_PARAMETER": 0,
+ "GRASS_VECTOR_DSCO": "",
+ "GRASS_VECTOR_LCO": "",
+ }
feedback = QgsProcessingFeedback()
results, ok = alg.run(parameters, context, feedback)
@@ -233,7 +247,7 @@ def testOutputToGeopackage(self):
self.assertTrue(os.path.exists(temp_file))
# make sure that layer has correct features
- res = QgsVectorLayer(temp_file, 'res')
+ res = QgsVectorLayer(temp_file, "res")
self.assertTrue(res.isValid())
self.assertEqual(res.featureCount(), 2)
@@ -241,73 +255,109 @@ def testOutputToGeopackage(self):
def testVectorLayerInput(self):
context = QgsProcessingContext()
- alg = QgsApplication.processingRegistry().createAlgorithmById('grass7:v.buffer')
+ alg = QgsApplication.processingRegistry().createAlgorithmById("grass7:v.buffer")
self.assertIsNotNone(alg)
self.assertFalse(alg.commands)
def get_command(alg):
command = alg.commands[-1]
command = re.sub(r'output=".*?"', 'output="###"', command)
- command = command.replace(testDataPath, 'testdata')
+ command = command.replace(testDataPath, "testdata")
return command
# GML source
- source = os.path.join(testDataPath, 'points.gml')
+ source = os.path.join(testDataPath, "points.gml")
vl = QgsVectorLayer(source)
self.assertTrue(vl.isValid())
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o',
+ )
# try with external -- not support for GML, so should fall back to v.in.ogr
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o',
+ )
# SHP source
- source = os.path.join(testDataPath, 'lines_z.shp')
+ source = os.path.join(testDataPath, "lines_z.shp")
vl = QgsVectorLayer(source)
self.assertTrue(vl.isValid())
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/lines_z.shp" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/lines_z.shp" output="###" --overwrite -o',
+ )
# try with external -- should work for shapefile
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.external input="testdata/lines_z.shp" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.external input="testdata/lines_z.shp" output="###" --overwrite -o',
+ )
# GPKG source
- source = os.path.join(testDataPath, 'pol.gpkg')
- vl = QgsVectorLayer(source + '|layername=pol2')
+ source = os.path.join(testDataPath, "pol.gpkg")
+ vl = QgsVectorLayer(source + "|layername=pol2")
self.assertTrue(vl.isValid())
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol2" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol2" output="###" --overwrite -o',
+ )
# try with external -- should work for Geopackage (although grass itself tends to crash here!)
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.external input="testdata/pol.gpkg" layer="pol2" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.external input="testdata/pol.gpkg" layer="pol2" output="###" --overwrite -o',
+ )
# different layer
- source = os.path.join(testDataPath, 'pol.gpkg')
- vl = QgsVectorLayer(source + '|layername=pol3')
+ source = os.path.join(testDataPath, "pol.gpkg")
+ vl = QgsVectorLayer(source + "|layername=pol3")
self.assertTrue(vl.isValid())
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o')
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.external input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o',
+ )
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.external input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o',
+ )
# GPKG no layer: you get what you get and you don't get upset
- source = os.path.join(testDataPath, 'pol.gpkg')
+ source = os.path.join(testDataPath, "pol.gpkg")
vl = QgsVectorLayer(source)
self.assertTrue(vl.isValid())
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" output="###" --overwrite -o')
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.external input="testdata/pol.gpkg" output="###" --overwrite -o')
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" output="###" --overwrite -o',
+ )
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.external input="testdata/pol.gpkg" output="###" --overwrite -o',
+ )
# layer with filter
- vl = QgsVectorLayer(source + '|layername=pol3')
+ vl = QgsVectorLayer(source + "|layername=pol3")
self.assertTrue(vl.isValid())
- vl.setSubsetString('"field"=\'value\'')
- alg.loadVectorLayer('test_layer', vl, context, external=False)
- self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o where="\\"field\\"=\'value\'"')
- alg.loadVectorLayer('test_layer', vl, context, external=True)
- self.assertEqual(get_command(alg), 'v.external input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o where="\\"field\\"=\'value\'"')
-
-
-if __name__ == '__main__':
+ vl.setSubsetString("\"field\"='value'")
+ alg.loadVectorLayer("test_layer", vl, context, external=False)
+ self.assertEqual(
+ get_command(alg),
+ 'v.in.ogr min_area=None snap=None input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o where="\\"field\\"=\'value\'"',
+ )
+ alg.loadVectorLayer("test_layer", vl, context, external=True)
+ self.assertEqual(
+ get_command(alg),
+ 'v.external input="testdata/pol.gpkg" layer="pol3" output="###" --overwrite -o where="\\"field\\"=\'value\'"',
+ )
+
+
+if __name__ == "__main__":
nose2.main()
diff --git a/python/plugins/processing/ProcessingPlugin.py b/python/plugins/processing/ProcessingPlugin.py
index 0b3452b638d9..07a743785087 100644
--- a/python/plugins/processing/ProcessingPlugin.py
+++ b/python/plugins/processing/ProcessingPlugin.py
@@ -15,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Victor Olaya'
-__date__ = 'August 2012'
-__copyright__ = '(C) 2012, Victor Olaya'
+__author__ = "Victor Olaya"
+__date__ = "August 2012"
+__copyright__ = "(C) 2012, Victor Olaya"
import shutil
import os
@@ -25,20 +25,24 @@
from typing import List
from functools import partial
-from qgis.core import (QgsApplication,
- QgsProcessingUtils,
- QgsProcessingModelAlgorithm,
- QgsProcessingAlgorithm,
- QgsDataItemProvider,
- QgsDataProvider,
- QgsDataItem,
- QgsMapLayerType,
- QgsMimeDataUtils,
- QgsSettings)
-from qgis.gui import (QgsGui,
- QgsOptionsWidgetFactory,
- QgsCustomDropHandler,
- QgsProcessingHistoryDialog)
+from qgis.core import (
+ QgsApplication,
+ QgsProcessingUtils,
+ QgsProcessingModelAlgorithm,
+ QgsProcessingAlgorithm,
+ QgsDataItemProvider,
+ QgsDataProvider,
+ QgsDataItem,
+ QgsMapLayerType,
+ QgsMimeDataUtils,
+ QgsSettings,
+)
+from qgis.gui import (
+ QgsGui,
+ QgsOptionsWidgetFactory,
+ QgsCustomDropHandler,
+ QgsProcessingHistoryDialog,
+)
from qgis.PyQt.QtCore import (
QObject,
Qt,
@@ -47,17 +51,10 @@
QDir,
QFileInfo,
pyqtSlot,
- QMetaObject
-)
-from qgis.PyQt.QtWidgets import (
- QWidget,
- QMenu,
- QAction
-)
-from qgis.PyQt.QtGui import (
- QIcon,
- QKeySequence
+ QMetaObject,
)
+from qgis.PyQt.QtWidgets import QWidget, QMenu, QAction
+from qgis.PyQt.QtGui import QIcon, QKeySequence
from qgis.utils import iface
from processing.core.Processing import Processing
@@ -66,8 +63,10 @@
from processing.gui.ResultsDock import ResultsDock
from processing.gui.MessageDialog import MessageDialog
from processing.gui.MessageBarProgress import MessageBarProgress
-from processing.gui.AlgorithmLocatorFilter import (AlgorithmLocatorFilter,
- InPlaceAlgorithmLocatorFilter)
+from processing.gui.AlgorithmLocatorFilter import (
+ AlgorithmLocatorFilter,
+ InPlaceAlgorithmLocatorFilter,
+)
from processing.gui.Postprocessing import handleAlgorithmResults
from processing.gui.AlgorithmExecutor import execute, execute_in_place
from processing.gui.AlgorithmDialog import AlgorithmDialog
@@ -76,7 +75,13 @@
from processing.modeler.ModelerDialog import ModelerDialog
from processing.tools.system import tempHelpFolder
from processing.tools import dataobjects
-from processing.gui.menus import removeMenus, initializeMenus, createMenus, createButtons, removeButtons
+from processing.gui.menus import (
+ removeMenus,
+ initializeMenus,
+ createMenus,
+ createButtons,
+ removeButtons,
+)
from processing.core.ProcessingResults import resultsList
pluginPath = os.path.dirname(__file__)
@@ -88,7 +93,7 @@ def __init__(self):
super(QgsOptionsWidgetFactory, self).__init__()
def icon(self):
- return QgsApplication.getThemeIcon('/processingAlgorithm.svg')
+ return QgsApplication.getThemeIcon("/processingAlgorithm.svg")
def createWidget(self, parent):
return ConfigOptionsPage(parent)
@@ -97,7 +102,7 @@ def createWidget(self, parent):
class ProcessingDropHandler(QgsCustomDropHandler):
def handleFileDrop(self, file):
- if not file.lower().endswith('.model3'):
+ if not file.lower().endswith(".model3"):
return False
return self.runAlg(file)
@@ -107,7 +112,7 @@ def runAlg(file):
if not alg.fromFile(file):
return False
- alg.setProvider(QgsApplication.processingRegistry().providerById('model'))
+ alg.setProvider(QgsApplication.processingRegistry().providerById("model"))
dlg = AlgorithmDialog(alg, parent=iface.mainWindow())
dlg.show()
# do NOT remove!!!! if you do then sip forgets the python subclass of AlgorithmDialog and you get a broken
@@ -116,7 +121,7 @@ def runAlg(file):
return True
def customUriProviderKey(self):
- return 'processing'
+ return "processing"
def handleCustomUriDrop(self, uri):
path = uri.uri
@@ -155,9 +160,13 @@ def editModel(self):
dlg.show()
def actions(self, parent):
- run_model_action = QAction(QCoreApplication.translate('ProcessingPlugin', '&Run Model…'), parent)
+ run_model_action = QAction(
+ QCoreApplication.translate("ProcessingPlugin", "&Run Model…"), parent
+ )
run_model_action.triggered.connect(self.runModel)
- edit_model_action = QAction(QCoreApplication.translate('ProcessingPlugin', '&Edit Model…'), parent)
+ edit_model_action = QAction(
+ QCoreApplication.translate("ProcessingPlugin", "&Edit Model…"), parent
+ )
edit_model_action.triggered.connect(self.editModel)
return [run_model_action, edit_model_action]
@@ -168,7 +177,7 @@ def __init__(self):
super().__init__()
def name(self):
- return 'processing'
+ return "processing"
def capabilities(self):
return QgsDataProvider.DataCapability.File
@@ -176,7 +185,7 @@ def capabilities(self):
def createDataItem(self, path, parentItem):
file_info = QFileInfo(path)
- if file_info.suffix().lower() == 'model3':
+ if file_info.suffix().lower() == "model3":
alg = QgsProcessingModelAlgorithm()
if alg.fromFile(path):
return ProcessingModelItem(parentItem, alg.name(), path)
@@ -194,7 +203,7 @@ def __init__(self, iface):
self.locator_filter = None
self.edit_features_locator_filter = None
self.initialized = False
- self._gui_connections: List[QMetaObject.Connection] = []
+ self._gui_connections: list[QMetaObject.Connection] = []
self.initProcessing()
def initProcessing(self):
@@ -209,13 +218,15 @@ def initGui(self):
# port old log, ONCE ONLY!
settings = QgsSettings()
if not settings.value("/Processing/hasPortedOldLog", False, bool):
- processing_history_provider = QgsGui.historyProviderRegistry().providerById('processing')
+ processing_history_provider = QgsGui.historyProviderRegistry().providerById(
+ "processing"
+ )
if processing_history_provider:
processing_history_provider.portOldLog()
settings.setValue("/Processing/hasPortedOldLog", True)
self.options_factory = ProcessingOptionsFactory()
- self.options_factory.setTitle(self.tr('Processing'))
+ self.options_factory.setTitle(self.tr("Processing"))
iface.registerOptionsWidgetFactory(self.options_factory)
self.drop_handler = ProcessingDropHandler()
iface.registerCustomDropHandler(self.drop_handler)
@@ -225,15 +236,17 @@ def initGui(self):
iface.registerLocatorFilter(self.locator_filter)
# Invalidate the locator filter for in-place when active layer changes
self._gui_connections.append(
- iface.currentLayerChanged.connect(lambda _: self.iface.invalidateLocatorResults())
+ iface.currentLayerChanged.connect(
+ lambda _: self.iface.invalidateLocatorResults()
+ )
)
self.edit_features_locator_filter = InPlaceAlgorithmLocatorFilter()
iface.registerLocatorFilter(self.edit_features_locator_filter)
- QgsGui.historyProviderRegistry().providerById('processing').executePython.connect(
- self._execute_history_commands
- )
- QgsGui.historyProviderRegistry().providerById('processing').createTest.connect(
+ QgsGui.historyProviderRegistry().providerById(
+ "processing"
+ ).executePython.connect(self._execute_history_commands)
+ QgsGui.historyProviderRegistry().providerById("processing").createTest.connect(
self.create_test
)
@@ -245,50 +258,69 @@ def initGui(self):
self.toolbox.executeWithGui.connect(self.executeAlgorithm)
self.resultsDock = ResultsDock()
- self.iface.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.resultsDock)
+ self.iface.addDockWidget(
+ Qt.DockWidgetArea.RightDockWidgetArea, self.resultsDock
+ )
self.resultsDock.hide()
self.menu = QMenu(self.iface.mainWindow().menuBar())
- self.menu.setObjectName('processing')
- self.menu.setTitle(self.tr('Pro&cessing'))
+ self.menu.setObjectName("processing")
+ self.menu.setTitle(self.tr("Pro&cessing"))
- self.toolboxAction = QAction(self.tr('&Toolbox'), self.iface.mainWindow())
+ self.toolboxAction = QAction(self.tr("&Toolbox"), self.iface.mainWindow())
self.toolboxAction.setCheckable(True)
- self.toolboxAction.setObjectName('toolboxAction')
+ self.toolboxAction.setObjectName("toolboxAction")
self.toolboxAction.setIcon(
- QgsApplication.getThemeIcon("/processingAlgorithm.svg"))
- self.iface.registerMainWindowAction(self.toolboxAction,
- QKeySequence('Ctrl+Alt+T').toString(QKeySequence.SequenceFormat.NativeText))
+ QgsApplication.getThemeIcon("/processingAlgorithm.svg")
+ )
+ self.iface.registerMainWindowAction(
+ self.toolboxAction,
+ QKeySequence("Ctrl+Alt+T").toString(QKeySequence.SequenceFormat.NativeText),
+ )
self.toolboxAction.toggled.connect(self.openToolbox)
- self.iface.attributesToolBar().insertAction(self.iface.actionOpenStatisticalSummary(), self.toolboxAction)
+ self.iface.attributesToolBar().insertAction(
+ self.iface.actionOpenStatisticalSummary(), self.toolboxAction
+ )
self.menu.addAction(self.toolboxAction)
self.modelerAction = QAction(
QgsApplication.getThemeIcon("/processingModel.svg"),
- QCoreApplication.translate('ProcessingPlugin', '&Model Designer…'), self.iface.mainWindow())
- self.modelerAction.setObjectName('modelerAction')
+ QCoreApplication.translate("ProcessingPlugin", "&Model Designer…"),
+ self.iface.mainWindow(),
+ )
+ self.modelerAction.setObjectName("modelerAction")
self.modelerAction.triggered.connect(self.openModeler)
- self.iface.registerMainWindowAction(self.modelerAction,
- QKeySequence('Ctrl+Alt+G').toString(QKeySequence.SequenceFormat.NativeText))
+ self.iface.registerMainWindowAction(
+ self.modelerAction,
+ QKeySequence("Ctrl+Alt+G").toString(QKeySequence.SequenceFormat.NativeText),
+ )
self.menu.addAction(self.modelerAction)
self.historyAction = QAction(
QgsApplication.getThemeIcon("/mIconHistory.svg"),
- QCoreApplication.translate('ProcessingPlugin', '&History…'), self.iface.mainWindow())
- self.historyAction.setObjectName('historyAction')
+ QCoreApplication.translate("ProcessingPlugin", "&History…"),
+ self.iface.mainWindow(),
+ )
+ self.historyAction.setObjectName("historyAction")
self.historyAction.triggered.connect(self.openHistory)
- self.iface.registerMainWindowAction(self.historyAction,
- QKeySequence('Ctrl+Alt+H').toString(QKeySequence.SequenceFormat.NativeText))
+ self.iface.registerMainWindowAction(
+ self.historyAction,
+ QKeySequence("Ctrl+Alt+H").toString(QKeySequence.SequenceFormat.NativeText),
+ )
self.menu.addAction(self.historyAction)
self.toolbox.processingToolbar.addAction(self.historyAction)
self.resultsAction = QAction(
QgsApplication.getThemeIcon("/processingResult.svg"),
- self.tr('&Results Viewer'), self.iface.mainWindow())
- self.resultsAction.setObjectName('resultsViewer')
+ self.tr("&Results Viewer"),
+ self.iface.mainWindow(),
+ )
+ self.resultsAction.setObjectName("resultsViewer")
self.resultsAction.setCheckable(True)
- self.iface.registerMainWindowAction(self.resultsAction,
- QKeySequence('Ctrl+Alt+R').toString(QKeySequence.SequenceFormat.NativeText))
+ self.iface.registerMainWindowAction(
+ self.resultsAction,
+ QKeySequence("Ctrl+Alt+R").toString(QKeySequence.SequenceFormat.NativeText),
+ )
self.menu.addAction(self.resultsAction)
self.toolbox.processingToolbar.addAction(self.resultsAction)
@@ -299,8 +331,10 @@ def initGui(self):
self.editInPlaceAction = QAction(
QgsApplication.getThemeIcon("/mActionProcessSelected.svg"),
- self.tr('Edit Features In-Place'), self.iface.mainWindow())
- self.editInPlaceAction.setObjectName('editInPlaceFeatures')
+ self.tr("Edit Features In-Place"),
+ self.iface.mainWindow(),
+ )
+ self.editInPlaceAction.setObjectName("editInPlaceFeatures")
self.editInPlaceAction.setCheckable(True)
self.editInPlaceAction.toggled.connect(self.editSelected)
self.menu.addAction(self.editInPlaceAction)
@@ -310,14 +344,15 @@ def initGui(self):
self.optionsAction = QAction(
QgsApplication.getThemeIcon("/mActionOptions.svg"),
- self.tr('Options'), self.iface.mainWindow())
- self.optionsAction.setObjectName('optionsAction')
+ self.tr("Options"),
+ self.iface.mainWindow(),
+ )
+ self.optionsAction.setObjectName("optionsAction")
self.optionsAction.triggered.connect(self.openProcessingOptions)
self.toolbox.processingToolbar.addAction(self.optionsAction)
menuBar = self.iface.mainWindow().menuBar()
- menuBar.insertMenu(
- self.iface.firstRightStandardMenu().menuAction(), self.menu)
+ menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(), self.menu)
self.menu.addSeparator()
@@ -334,10 +369,14 @@ def initGui(self):
self.iface.currentLayerChanged.connect(self.sync_in_place_button_state)
)
self._gui_connections.append(
- self.iface.mapCanvas().selectionChanged.connect(self.sync_in_place_button_state)
+ self.iface.mapCanvas().selectionChanged.connect(
+ self.sync_in_place_button_state
+ )
)
self._gui_connections.append(
- self.iface.actionToggleEditing().triggered.connect(partial(self.sync_in_place_button_state, None))
+ self.iface.actionToggleEditing().triggered.connect(
+ partial(self.sync_in_place_button_state, None)
+ )
)
self.sync_in_place_button_state()
@@ -346,7 +385,9 @@ def initGui(self):
self.projectMenuAction = None
self.projectMenuSeparator = None
- self.projectProvider = QgsApplication.instance().processingRegistry().providerById("project")
+ self.projectProvider = (
+ QgsApplication.instance().processingRegistry().providerById("project")
+ )
self._gui_connections.append(
self.projectProvider.algorithmsLoaded.connect(self.updateProjectModelMenu)
)
@@ -356,7 +397,9 @@ def updateProjectModelMenu(self):
if self.projectMenuAction is None:
self.projectModelsMenu = QMenu(self.tr("Models"))
- self.projectMenuAction = self.iface.projectMenu().insertMenu(self.iface.projectMenu().children()[-1], self.projectModelsMenu)
+ self.projectMenuAction = self.iface.projectMenu().insertMenu(
+ self.iface.projectMenu().children()[-1], self.projectModelsMenu
+ )
self.projectMenuAction.setParent(self.projectModelsMenu)
self.iface.projectMenu().insertSeparator(self.projectMenuAction)
@@ -366,12 +409,27 @@ def updateProjectModelMenu(self):
modelSubMenu = self.projectModelsMenu.addMenu(model.name())
modelSubMenu.setParent(self.projectModelsMenu)
action = QAction(self.tr("Execute…"), modelSubMenu)
- action.triggered.connect(partial(self.executeAlgorithm, model.id(), self.projectModelsMenu, self.toolbox.in_place_mode))
+ action.triggered.connect(
+ partial(
+ self.executeAlgorithm,
+ model.id(),
+ self.projectModelsMenu,
+ self.toolbox.in_place_mode,
+ )
+ )
modelSubMenu.addAction(action)
if model.flags() & QgsProcessingAlgorithm.Flag.FlagSupportsBatch:
action = QAction(self.tr("Execute as Batch Process…"), modelSubMenu)
modelSubMenu.addAction(action)
- action.triggered.connect(partial(self.executeAlgorithm, model.id(), self.projectModelsMenu, self.toolbox.in_place_mode, True))
+ action.triggered.connect(
+ partial(
+ self.executeAlgorithm,
+ model.id(),
+ self.projectModelsMenu,
+ self.toolbox.in_place_mode,
+ True,
+ )
+ )
@pyqtSlot(str, QWidget, bool, bool)
def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
@@ -389,19 +447,25 @@ def executeAlgorithm(self, alg_id, parent, in_place=False, as_batch=False):
config = {}
if in_place:
- config['IN_PLACE'] = True
+ config["IN_PLACE"] = True
- alg = QgsApplication.instance().processingRegistry().createAlgorithmById(alg_id, config)
+ alg = (
+ QgsApplication.instance()
+ .processingRegistry()
+ .createAlgorithmById(alg_id, config)
+ )
if alg is not None:
ok, message = alg.canExecute()
if not ok:
dlg = MessageDialog()
- dlg.setTitle(self.tr('Error executing algorithm'))
+ dlg.setTitle(self.tr("Error executing algorithm"))
dlg.setMessage(
- self.tr('
\n")
for out in self.algorithm().outputDefinitions():
if out.name() in results:
- f.write(f'
{out.description()}
{results[out.name()]}
\n')
- f.write('
\n')
+ f.write(
+ f"
{out.description()}
{results[out.name()]}
\n"
+ )
+ f.write("
\n")
if errors:
- f.write('
{}
\n'.format(self.tr('Errors')))
+ f.write('
{}
\n'.format(self.tr("Errors")))
for i, res in enumerate(errors):
- errors = res['errors']
- params = res['parameters']
+ errors = res["errors"]
+ params = res["parameters"]
if i > 0:
- f.write('\n')
- f.write(self.tr('
Parameters
\n'))
- f.write('
\n')
+ f.write("\n")
+ f.write(self.tr("
Parameters
\n"))
+ f.write("
\n")
for param in self.algorithm().parameterDefinitions():
if not param.isDestination():
if param.name() in params:
f.write(
- f'
%s" % (self.tr("Obsolete plugin:"), plugin["name"], self.tr("QGIS has detected an obsolete plugin that masks its more recent version shipped with this copy of QGIS. This is likely due to files associated with a previous installation of QGIS. Do you want to remove the old plugin right now and unmask the more recent version?")))
+ msg.addButton(
+ self.tr("Uninstall (recommended)"), QMessageBox.ButtonRole.AcceptRole
+ )
+ msg.addButton(
+ self.tr("I will uninstall it later"), QMessageBox.ButtonRole.RejectRole
+ )
+ msg.setText(
+ "{} {}
{}".format(
+ self.tr("Obsolete plugin:"),
+ plugin["name"],
+ self.tr(
+ "QGIS has detected an obsolete plugin that masks its more recent version shipped with this copy of QGIS. This is likely due to files associated with a previous installation of QGIS. Do you want to remove the old plugin right now and unmask the more recent version?"
+ ),
+ )
+ )
msg.exec()
if not msg.result():
settings = QgsSettings()
- plugin_is_active = settings.value("/PythonPlugins/" + key, False, type=bool)
+ plugin_is_active = settings.value(
+ "/PythonPlugins/" + key, False, type=bool
+ )
# uninstall the update, update utils and reload if enabled
self.uninstallPlugin(key, quiet=True)
updateAvailablePlugins()
if plugin_is_active:
- settings.setValue("/PythonPlugins/watchDogTimestamp/" + key,
- QDateTime.currentDateTime().toSecsSinceEpoch())
+ settings.setValue(
+ "/PythonPlugins/watchDogTimestamp/" + key,
+ QDateTime.currentDateTime().toSecsSinceEpoch(),
+ )
loadPlugin(key)
startPlugin(key)
settings.remove("/PythonPlugins/watchDogTimestamp/" + key)
# ----------------------------------------- #
def fetchAvailablePlugins(self, reloadMode):
- """ Fetch plugins from all enabled repositories."""
+ """Fetch plugins from all enabled repositories."""
""" reloadMode = true: Fully refresh data from QgsSettings to mRepositories """
""" reloadMode = false: Fetch unready repositories only """
if reloadMode:
@@ -135,7 +160,9 @@ def fetchAvailablePlugins(self, reloadMode):
plugins.getAllInstalled()
for key in repositories.allEnabled():
- if reloadMode or repositories.all()[key]["state"] == 3: # if state = 3 (error or not fetched yet), try to fetch once again
+ if (
+ reloadMode or repositories.all()[key]["state"] == 3
+ ): # if state = 3 (error or not fetched yet), try to fetch once again
repositories.requestFetching(key, force_reload=reloadMode)
if repositories.fetchingInProgress():
@@ -146,19 +173,34 @@ def fetchAvailablePlugins(self, reloadMode):
repositories.killConnection(key)
# display error messages for every unavailable repository, unless Shift pressed nor all repositories are unavailable
- keepQuiet = QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(Qt.KeyboardModifier.ShiftModifier)
- if repositories.allUnavailable() and repositories.allUnavailable() != repositories.allEnabled():
+ keepQuiet = QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(
+ Qt.KeyboardModifier.ShiftModifier
+ )
+ if (
+ repositories.allUnavailable()
+ and repositories.allUnavailable() != repositories.allEnabled()
+ ):
for key in repositories.allUnavailable():
if not keepQuiet:
- QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self.tr("Error reading repository:") + " " + key + "\n\n" + repositories.all()[key]["error"])
- if QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(Qt.KeyboardModifier.ShiftModifier):
+ QMessageBox.warning(
+ iface.mainWindow(),
+ self.tr("QGIS Python Plugin Installer"),
+ self.tr("Error reading repository:")
+ + " "
+ + key
+ + "\n\n"
+ + repositories.all()[key]["error"],
+ )
+ if QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(
+ Qt.KeyboardModifier.ShiftModifier
+ ):
keepQuiet = True
# finally, rebuild plugins from the caches
plugins.rebuild()
# ----------------------------------------- #
def checkingDone(self):
- """ Remove the "Looking for new plugins..." label and display a notification instead if any updates available """
+ """Remove the "Looking for new plugins..." label and display a notification instead if any updates available"""
# rebuild plugins cache
plugins.rebuild()
@@ -176,104 +218,120 @@ def checkingDone(self):
if len(updatable_plugin_names) >= 2:
status = self.tr("Multiple plugin updates are available")
else:
- status = self.tr("An update to the {} plugin is available").format(updatable_plugin_names[0])
+ status = self.tr("An update to the {} plugin is available").format(
+ updatable_plugin_names[0]
+ )
QgsMessageLog.logMessage(
- "Plugin update(s) available : {}".format(','.join(updatable_plugin_names)), self.tr("Plugins"))
+ "Plugin update(s) available : {}".format(",".join(updatable_plugin_names)),
+ self.tr("Plugins"),
+ )
bar = iface.messageBar()
- self.message_bar_widget = bar.createMessage('', status)
+ self.message_bar_widget = bar.createMessage("", status)
update_button = QPushButton(self.tr("Install Updates…"))
tab_index = 3 # PLUGMAN_TAB_UPGRADEABLE
- update_button.pressed.connect(partial(self.showPluginManagerWhenReady, tab_index))
+ update_button.pressed.connect(
+ partial(self.showPluginManagerWhenReady, tab_index)
+ )
self.message_bar_widget.layout().addWidget(update_button)
bar.pushWidget(self.message_bar_widget, Qgis.MessageLevel.Info)
# ----------------------------------------- #
def exportRepositoriesToManager(self):
- """ Update manager's repository tree widget with current data """
+ """Update manager's repository tree widget with current data"""
iface.pluginManagerInterface().clearRepositoryList()
for key in repositories.all():
url = repositories.all()[key]["url"] + repositories.urlParams()
if repositories.inspectionFilter():
- enabled = (key == repositories.inspectionFilter())
+ enabled = key == repositories.inspectionFilter()
else:
enabled = repositories.all()[key]["enabled"]
- iface.pluginManagerInterface().addToRepositoryList({
- "name": key,
- "url": url,
- "enabled": enabled and "true" or "false",
- "valid": repositories.all()[key]["valid"] and "true" or "false",
- "state": str(repositories.all()[key]["state"]),
- "error": repositories.all()[key]["error"],
- "inspection_filter": repositories.inspectionFilter() and "true" or "false"
- })
+ iface.pluginManagerInterface().addToRepositoryList(
+ {
+ "name": key,
+ "url": url,
+ "enabled": enabled and "true" or "false",
+ "valid": repositories.all()[key]["valid"] and "true" or "false",
+ "state": str(repositories.all()[key]["state"]),
+ "error": repositories.all()[key]["error"],
+ "inspection_filter": repositories.inspectionFilter()
+ and "true"
+ or "false",
+ }
+ )
# ----------------------------------------- #
def exportPluginsToManager(self):
- """ Insert plugins metadata to QgsMetadataRegistry """
+ """Insert plugins metadata to QgsMetadataRegistry"""
iface.pluginManagerInterface().clearPythonPluginMetadata()
for key in plugins.all():
plugin = plugins.all()[key]
- iface.pluginManagerInterface().addPluginMetadata({
- "id": key,
- "plugin_id": plugin["plugin_id"] or "",
- "name": plugin["name"],
- "description": plugin["description"],
- "about": plugin["about"],
- "category": plugin["category"],
- "tags": plugin["tags"],
- "changelog": plugin["changelog"],
- "author_name": plugin["author_name"],
- "author_email": plugin["author_email"],
- "homepage": plugin["homepage"],
- "tracker": plugin["tracker"],
- "code_repository": plugin["code_repository"],
- "version_installed": plugin["version_installed"],
- "library": plugin["library"],
- "icon": plugin["icon"],
- "readonly": plugin["readonly"] and "true" or "false",
- "installed": plugin["installed"] and "true" or "false",
- "available": plugin["available"] and "true" or "false",
- "status": plugin["status"],
- "status_exp": plugin["status_exp"],
- "error": plugin["error"],
- "error_details": plugin["error_details"],
- "create_date": plugin["create_date"],
- "update_date": plugin["update_date"],
- "create_date_stable": plugin["create_date_stable"],
- "update_date_stable": plugin["update_date_stable"],
- "create_date_experimental": plugin["create_date_experimental"],
- "update_date_experimental": plugin["update_date_experimental"],
- "experimental": plugin["experimental"] and "true" or "false",
- "deprecated": plugin["deprecated"] and "true" or "false",
- "trusted": plugin["trusted"] and "true" or "false",
- "version_available": plugin["version_available"],
- "version_available_stable": plugin["version_available_stable"] or "",
- "version_available_experimental": plugin["version_available_experimental"] or "",
- "zip_repository": plugin["zip_repository"],
- "download_url": plugin["download_url"],
- "download_url_stable": plugin["download_url_stable"],
- "download_url_experimental": plugin["download_url_experimental"],
- "filename": plugin["filename"],
- "downloads": plugin["downloads"],
- "average_vote": plugin["average_vote"],
- "rating_votes": plugin["rating_votes"],
- "plugin_dependencies": plugin.get("plugin_dependencies", None),
- "pythonic": "true"
- })
+ iface.pluginManagerInterface().addPluginMetadata(
+ {
+ "id": key,
+ "plugin_id": plugin["plugin_id"] or "",
+ "name": plugin["name"],
+ "description": plugin["description"],
+ "about": plugin["about"],
+ "category": plugin["category"],
+ "tags": plugin["tags"],
+ "changelog": plugin["changelog"],
+ "author_name": plugin["author_name"],
+ "author_email": plugin["author_email"],
+ "homepage": plugin["homepage"],
+ "tracker": plugin["tracker"],
+ "code_repository": plugin["code_repository"],
+ "version_installed": plugin["version_installed"],
+ "library": plugin["library"],
+ "icon": plugin["icon"],
+ "readonly": plugin["readonly"] and "true" or "false",
+ "installed": plugin["installed"] and "true" or "false",
+ "available": plugin["available"] and "true" or "false",
+ "status": plugin["status"],
+ "status_exp": plugin["status_exp"],
+ "error": plugin["error"],
+ "error_details": plugin["error_details"],
+ "create_date": plugin["create_date"],
+ "update_date": plugin["update_date"],
+ "create_date_stable": plugin["create_date_stable"],
+ "update_date_stable": plugin["update_date_stable"],
+ "create_date_experimental": plugin["create_date_experimental"],
+ "update_date_experimental": plugin["update_date_experimental"],
+ "experimental": plugin["experimental"] and "true" or "false",
+ "deprecated": plugin["deprecated"] and "true" or "false",
+ "trusted": plugin["trusted"] and "true" or "false",
+ "version_available": plugin["version_available"],
+ "version_available_stable": plugin["version_available_stable"]
+ or "",
+ "version_available_experimental": plugin[
+ "version_available_experimental"
+ ]
+ or "",
+ "zip_repository": plugin["zip_repository"],
+ "download_url": plugin["download_url"],
+ "download_url_stable": plugin["download_url_stable"],
+ "download_url_experimental": plugin["download_url_experimental"],
+ "filename": plugin["filename"],
+ "downloads": plugin["downloads"],
+ "average_vote": plugin["average_vote"],
+ "rating_votes": plugin["rating_votes"],
+ "plugin_dependencies": plugin.get("plugin_dependencies", None),
+ "pythonic": "true",
+ }
+ )
iface.pluginManagerInterface().reloadModel()
# ----------------------------------------- #
def reloadAndExportData(self):
- """ Reload All repositories and export data to the Plugin Manager """
+ """Reload All repositories and export data to the Plugin Manager"""
self.fetchAvailablePlugins(reloadMode=True)
self.exportRepositoriesToManager()
self.exportPluginsToManager()
# ----------------------------------------- #
- def showPluginManagerWhenReady(self, * params):
- """ Open the plugin manager window. If fetching is still in progress, it shows the progress window first """
+ def showPluginManagerWhenReady(self, *params):
+ """Open the plugin manager window. If fetching is still in progress, it shows the progress window first"""
""" Optionally pass the index of tab to be opened in params """
if self.message_bar_widget:
if not sip.isdeleted(self.message_bar_widget):
@@ -294,34 +352,47 @@ def showPluginManagerWhenReady(self, * params):
# ----------------------------------------- #
def onManagerClose(self):
- """ Call this method when closing manager window - it resets last-use-dependent values. """
+ """Call this method when closing manager window - it resets last-use-dependent values."""
plugins.updateSeenPluginsList()
repositories.saveCheckingOnStartLastDate()
# ----------------------------------------- #
def exportSettingsGroup(self):
- """ Return QgsSettings settingsGroup value """
+ """Return QgsSettings settingsGroup value"""
# todo QGIS 4 remove
return "plugin-manager"
# ----------------------------------------- #
def upgradeAllUpgradeable(self):
- """ Reinstall all upgradeable plugins """
+ """Reinstall all upgradeable plugins"""
for key in plugins.allUpgradeable():
self.installPlugin(key, quiet=True)
# ----------------------------------------- #
def installPlugin(self, key, quiet=False, stable=True):
- """ Install given plugin """
+ """Install given plugin"""
error = False
- status_key = 'status' if stable else 'status_exp'
- infoString = ('', '')
+ status_key = "status" if stable else "status_exp"
+ infoString = ("", "")
plugin = plugins.all()[key]
previousStatus = plugin[status_key]
if not plugin:
return
- if plugin[status_key] == "newer" and not plugin["error"]: # ask for confirmation if user downgrades an usable plugin
- if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self.tr("Are you sure you want to downgrade the plugin to the latest available version? The installed one is newer!"), QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ if (
+ plugin[status_key] == "newer" and not plugin["error"]
+ ): # ask for confirmation if user downgrades an usable plugin
+ if (
+ QMessageBox.warning(
+ iface.mainWindow(),
+ self.tr("QGIS Python Plugin Installer"),
+ self.tr(
+ "Are you sure you want to downgrade the plugin to the latest available version? The installed one is newer!"
+ ),
+ QMessageBox.StandardButton.Yes,
+ QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return
# if plugin is active, unload it before update, see https://github.com/qgis/QGIS/issues/54968
@@ -329,7 +400,9 @@ def installPlugin(self, key, quiet=False, stable=True):
if pluginWasLoaded:
unloadPlugin(plugin["id"])
- dlg = QgsPluginInstallerInstallingDialog(iface.mainWindow(), plugin, stable=stable)
+ dlg = QgsPluginInstallerInstallingDialog(
+ iface.mainWindow(), plugin, stable=stable
+ )
dlg.exec()
plugin_path = HOME_PLUGIN_PATH + "/" + key
@@ -344,11 +417,13 @@ def installPlugin(self, key, quiet=False, stable=True):
infoString = (
self.tr("Plugin has disappeared"),
self.tr(
- "The plugin seems to have been installed but it's not possible to know where. The directory \"{}\" "
+ 'The plugin seems to have been installed but it\'s not possible to know where. The directory "{}" '
"has not been found. Probably the plugin package contained a wrong named directory.\nPlease search "
"the list of installed plugins. You should find the plugin there, but it's not possible to "
"determine which of them it is and it's also not possible to inform you about available updates. "
- "Please contact the plugin author and submit this issue.").format(plugin_path))
+ "Please contact the plugin author and submit this issue."
+ ).format(plugin_path),
+ )
with OverrideCursor(Qt.CursorShape.WaitCursor):
plugins.getAllInstalled()
plugins.rebuild()
@@ -375,7 +450,9 @@ def installPlugin(self, key, quiet=False, stable=True):
loadPlugin(plugin["id"])
startPlugin(plugin["id"])
else:
- unloadPlugin(key) # Just for a case. Will exit quietly if really not loaded
+ unloadPlugin(
+ key
+ ) # Just for a case. Will exit quietly if really not loaded
loadPlugin(key)
if quiet:
infoString = (None, None)
@@ -383,10 +460,14 @@ def installPlugin(self, key, quiet=False, stable=True):
else:
QApplication.restoreOverrideCursor()
if plugin["error"] == "incompatible":
- message = self.tr("The plugin is not compatible with this version of QGIS. It's designed for QGIS versions:")
+ message = self.tr(
+ "The plugin is not compatible with this version of QGIS. It's designed for QGIS versions:"
+ )
message += " " + plugin["error_details"] + ""
elif plugin["error"] == "dependent":
- message = self.tr("The plugin depends on some components missing on your system. You need to install the following Python module in order to enable it:")
+ message = self.tr(
+ "The plugin depends on some components missing on your system. You need to install the following Python module in order to enable it:"
+ )
message += " " + plugin["error_details"] + ""
else:
message = self.tr("The plugin is broken. Python said:")
@@ -425,7 +506,7 @@ def installPlugin(self, key, quiet=False, stable=True):
# ----------------------------------------- #
def uninstallPlugin(self, key, quiet=False):
- """ Uninstall given plugin """
+ """Uninstall given plugin"""
if key in plugins.all():
plugin = plugins.all()[key]
else:
@@ -433,10 +514,30 @@ def uninstallPlugin(self, key, quiet=False):
if not plugin:
return
if not quiet:
- warning = self.tr("Are you sure you want to uninstall the following plugin?") + "\n(" + plugin["name"] + ")"
- if plugin["status"] == "orphan" and plugin["status_exp"] == "orphan" and not plugin["error"]:
- warning += "\n\n" + self.tr("Warning: this plugin isn't available in any accessible repository!")
- if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ warning = (
+ self.tr("Are you sure you want to uninstall the following plugin?")
+ + "\n("
+ + plugin["name"]
+ + ")"
+ )
+ if (
+ plugin["status"] == "orphan"
+ and plugin["status_exp"] == "orphan"
+ and not plugin["error"]
+ ):
+ warning += "\n\n" + self.tr(
+ "Warning: this plugin isn't available in any accessible repository!"
+ )
+ if (
+ QMessageBox.warning(
+ iface.mainWindow(),
+ self.tr("QGIS Python Plugin Installer"),
+ warning,
+ QMessageBox.StandardButton.Yes,
+ QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return
# unload the plugin
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
@@ -448,7 +549,7 @@ def uninstallPlugin(self, key, quiet=False):
result = removeDir(pluginDir)
if result:
QApplication.restoreOverrideCursor()
- msg = "%s:%s" % (self.tr("Plugin uninstall failed"), result)
+ msg = "{}:{}".format(self.tr("Plugin uninstall failed"), result)
iface.pluginManagerInterface().pushMessage(msg, Qgis.MessageLevel.Critical)
else:
# safe remove
@@ -474,14 +575,16 @@ def uninstallPlugin(self, key, quiet=False):
plugins.rebuild()
self.exportPluginsToManager()
QApplication.restoreOverrideCursor()
- iface.pluginManagerInterface().pushMessage(self.tr("Plugin uninstalled successfully"), Qgis.MessageLevel.Success)
+ iface.pluginManagerInterface().pushMessage(
+ self.tr("Plugin uninstalled successfully"), Qgis.MessageLevel.Success
+ )
settings = QgsSettings()
settings.remove("/PythonPlugins/" + key)
# ----------------------------------------- #
def addRepository(self):
- """ add new repository connection """
+ """add new repository connection"""
dlg = QgsPluginInstallerRepositoryDialog(iface.mainWindow())
dlg.editParams.setText(repositories.urlParams())
dlg.checkBoxEnabled.setCheckState(Qt.CheckState.Checked)
@@ -489,7 +592,10 @@ def addRepository(self):
return
for i in list(repositories.all().values()):
if dlg.editURL.text().strip() == i["url"]:
- iface.pluginManagerInterface().pushMessage(self.tr("Unable to add another repository with the same URL!"), Qgis.MessageLevel.Warning)
+ iface.pluginManagerInterface().pushMessage(
+ self.tr("Unable to add another repository with the same URL!"),
+ Qgis.MessageLevel.Warning,
+ )
return
settings = QgsSettings()
settings.beginGroup(reposGroup)
@@ -500,14 +606,16 @@ def addRepository(self):
# add to settings
settings.setValue(reposName + "/url", reposURL)
settings.setValue(reposName + "/authcfg", dlg.editAuthCfg.text().strip())
- settings.setValue(reposName + "/enabled", bool(dlg.checkBoxEnabled.checkState()))
+ settings.setValue(
+ reposName + "/enabled", bool(dlg.checkBoxEnabled.checkState())
+ )
# refresh lists and populate widgets
plugins.removeRepository(reposName)
self.reloadAndExportData()
# ----------------------------------------- #
def editRepository(self, reposName):
- """ edit repository connection """
+ """edit repository connection"""
if not reposName:
return
checkState = {False: Qt.CheckState.Unchecked, True: Qt.CheckState.Checked}
@@ -516,19 +624,31 @@ def editRepository(self, reposName):
dlg.editURL.setText(repositories.all()[reposName]["url"])
dlg.editAuthCfg.setText(repositories.all()[reposName]["authcfg"])
dlg.editParams.setText(repositories.urlParams())
- dlg.checkBoxEnabled.setCheckState(checkState[repositories.all()[reposName]["enabled"]])
+ dlg.checkBoxEnabled.setCheckState(
+ checkState[repositories.all()[reposName]["enabled"]]
+ )
if repositories.all()[reposName]["valid"]:
dlg.checkBoxEnabled.setEnabled(True)
dlg.labelInfo.setText("")
else:
dlg.checkBoxEnabled.setEnabled(False)
- dlg.labelInfo.setText(self.tr("This repository is blocked due to incompatibility with your QGIS version"))
+ dlg.labelInfo.setText(
+ self.tr(
+ "This repository is blocked due to incompatibility with your QGIS version"
+ )
+ )
dlg.labelInfo.setFrameShape(QFrame.Shape.Box)
if not dlg.exec():
return # nothing to do if canceled
for i in list(repositories.all().values()):
- if dlg.editURL.text().strip() == i["url"] and dlg.editURL.text().strip() != repositories.all()[reposName]["url"]:
- iface.pluginManagerInterface().pushMessage(self.tr("Unable to add another repository with the same URL!"), Qgis.MessageLevel.Warning)
+ if (
+ dlg.editURL.text().strip() == i["url"]
+ and dlg.editURL.text().strip() != repositories.all()[reposName]["url"]
+ ):
+ iface.pluginManagerInterface().pushMessage(
+ self.tr("Unable to add another repository with the same URL!"),
+ Qgis.MessageLevel.Warning,
+ )
return
# delete old repo from QgsSettings and create new one
settings = QgsSettings()
@@ -542,7 +662,11 @@ def editRepository(self, reposName):
settings.setValue(newName + "/enabled", bool(dlg.checkBoxEnabled.checkState()))
if dlg.editAuthCfg.text().strip() != repositories.all()[reposName]["authcfg"]:
repositories.all()[reposName]["authcfg"] = dlg.editAuthCfg.text().strip()
- if dlg.editURL.text().strip() == repositories.all()[reposName]["url"] and dlg.checkBoxEnabled.checkState() == checkState[repositories.all()[reposName]["enabled"]]:
+ if (
+ dlg.editURL.text().strip() == repositories.all()[reposName]["url"]
+ and dlg.checkBoxEnabled.checkState()
+ == checkState[repositories.all()[reposName]["enabled"]]
+ ):
repositories.rename(reposName, newName)
self.exportRepositoriesToManager()
return # nothing else to do if only repository name was changed
@@ -551,16 +675,34 @@ def editRepository(self, reposName):
# ----------------------------------------- #
def deleteRepository(self, reposName: str):
- """ delete repository connection """
+ """delete repository connection"""
if not reposName:
return
settings = QgsSettings()
settings.beginGroup(reposGroup)
if settings.value(reposName + "/url", "", type=str) == officialRepo[1]:
- iface.pluginManagerInterface().pushMessage(self.tr("You can't remove the official QGIS Plugin Repository. You can disable it if needed."), Qgis.MessageLevel.Warning)
+ iface.pluginManagerInterface().pushMessage(
+ self.tr(
+ "You can't remove the official QGIS Plugin Repository. You can disable it if needed."
+ ),
+ Qgis.MessageLevel.Warning,
+ )
return
- warning = self.tr("Are you sure you want to remove the following repository?") + "\n" + reposName
- if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No) == QMessageBox.StandardButton.No:
+ warning = (
+ self.tr("Are you sure you want to remove the following repository?")
+ + "\n"
+ + reposName
+ )
+ if (
+ QMessageBox.warning(
+ iface.mainWindow(),
+ self.tr("QGIS Python Plugin Installer"),
+ warning,
+ QMessageBox.StandardButton.Yes,
+ QMessageBox.StandardButton.No,
+ )
+ == QMessageBox.StandardButton.No
+ ):
return
# delete from the settings, refresh data and repopulate all the widgets
settings.remove(reposName)
@@ -570,23 +712,39 @@ def deleteRepository(self, reposName: str):
# ----------------------------------------- #
def setRepositoryInspectionFilter(self, reposName=None):
- """ temporarily block another repositories to fetch only one for inspection """
+ """temporarily block another repositories to fetch only one for inspection"""
repositories.setInspectionFilter(reposName)
self.reloadAndExportData()
# ----------------------------------------- #
def sendVote(self, plugin_id, vote):
- """ send vote via the RPC """
+ """send vote via the RPC"""
if not plugin_id or not vote:
return False
url = "https://plugins.qgis.org/plugins/RPC2/"
- params = {"id": "djangorpc", "method": "plugin.vote", "params": [str(plugin_id), str(vote)]}
+ params = {
+ "id": "djangorpc",
+ "method": "plugin.vote",
+ "params": [str(plugin_id), str(vote)],
+ }
req = QNetworkRequest(QUrl(url))
- req.setAttribute(QNetworkRequest.Attribute(QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass), "QgsPluginInstaller")
- req.setAttribute(QNetworkRequest.Attribute(QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorRequestId), "sendVote")
+ req.setAttribute(
+ QNetworkRequest.Attribute(
+ QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass
+ ),
+ "QgsPluginInstaller",
+ )
+ req.setAttribute(
+ QNetworkRequest.Attribute(
+ QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorRequestId
+ ),
+ "sendVote",
+ )
req.setRawHeader(b"Content-Type", b"application/json")
- reply = QgsNetworkAccessManager.instance().blockingPost(req, bytes(json.dumps(params), "utf-8"))
+ reply = QgsNetworkAccessManager.instance().blockingPost(
+ req, bytes(json.dumps(params), "utf-8")
+ )
if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) == 200:
return True
else:
@@ -596,13 +754,17 @@ def installFromZipFile(self, filePath):
if not os.path.isfile(filePath):
return
- QgsSettingsTree.node("plugin-manager").childSetting("last-zip-directory").setValue(QFileInfo(filePath).absoluteDir().absolutePath())
+ QgsSettingsTree.node("plugin-manager").childSetting(
+ "last-zip-directory"
+ ).setValue(QFileInfo(filePath).absoluteDir().absolutePath())
pluginName = None
- with zipfile.ZipFile(filePath, 'r') as zf:
+ with zipfile.ZipFile(filePath, "r") as zf:
# search for metadata.txt. In case of multiple files, we can assume that
# the shortest path relates /metadata.txt
- metadatafiles = sorted(f for f in zf.namelist() if f.endswith('metadata.txt'))
+ metadatafiles = sorted(
+ f for f in zf.namelist() if f.endswith("metadata.txt")
+ )
if len(metadatafiles) > 0:
pluginName = os.path.split(metadatafiles[0])[0]
@@ -611,10 +773,18 @@ def installFromZipFile(self, filePath):
if not pluginName:
msg_box = QMessageBox()
msg_box.setIcon(QMessageBox.Icon.Warning)
- msg_box.setWindowTitle(self.tr("QGIS Python Install from ZIP Plugin Installer"))
- msg_box.setText(self.tr("The Zip file is not a valid QGIS python plugin. No root folder was found inside."))
+ msg_box.setWindowTitle(
+ self.tr("QGIS Python Install from ZIP Plugin Installer")
+ )
+ msg_box.setText(
+ self.tr(
+ "The Zip file is not a valid QGIS python plugin. No root folder was found inside."
+ )
+ )
msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
- more_info_btn = msg_box.addButton(self.tr("More Information"), QMessageBox.ButtonRole.HelpRole)
+ more_info_btn = msg_box.addButton(
+ self.tr("More Information"), QMessageBox.ButtonRole.HelpRole
+ )
msg_box.exec()
if msg_box.clickedButton() == more_info_btn:
QgsHelp.openHelp("plugins/plugins.html#the-install-from-zip-tab")
@@ -651,16 +821,24 @@ def installFromZipFile(self, filePath):
success = True
except Exception as e:
success = False
- if 'password' in str(e):
- infoString = self.tr('Aborted by user')
- if 'Bad password' in str(e):
- msg = self.tr('Wrong password. Please enter a correct password to the zip file.')
+ if "password" in str(e):
+ infoString = self.tr("Aborted by user")
+ if "Bad password" in str(e):
+ msg = self.tr(
+ "Wrong password. Please enter a correct password to the zip file."
+ )
else:
- msg = self.tr('The zip file is encrypted. Please enter password.')
+ msg = self.tr(
+ "The zip file is encrypted. Please enter password."
+ )
# Display a password dialog with QgsPasswordLineEdit
dlg = QDialog()
- dlg.setWindowTitle(self.tr('Enter password'))
- buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, Qt.Orientation.Horizontal)
+ dlg.setWindowTitle(self.tr("Enter password"))
+ buttonBox = QDialogButtonBox(
+ QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel,
+ Qt.Orientation.Horizontal,
+ )
buttonBox.rejected.connect(dlg.reject)
buttonBox.accepted.connect(dlg.accept)
lePass = QgsPasswordLineEdit()
@@ -672,7 +850,9 @@ def installFromZipFile(self, filePath):
keepTrying = dlg.exec()
password = lePass.text()
else:
- infoString = self.tr("Failed to unzip the plugin package\n{}.\nProbably it is broken".format(filePath))
+ infoString = self.tr(
+ f"Failed to unzip the plugin package\n{filePath}.\nProbably it is broken"
+ )
keepTrying = False
if success:
@@ -684,19 +864,25 @@ def installFromZipFile(self, filePath):
plugins.rebuild()
settings = QgsSettings()
- if settings.contains('/PythonPlugins/' + pluginName): # Plugin was available?
+ if settings.contains(
+ "/PythonPlugins/" + pluginName
+ ): # Plugin was available?
unloadPlugin(pluginName)
loadPlugin(pluginName)
- if settings.value('/PythonPlugins/' + pluginName, False, bool): # Plugin was also active?
+ if settings.value(
+ "/PythonPlugins/" + pluginName, False, bool
+ ): # Plugin was also active?
startPlugin(pluginName)
else:
if startPlugin(pluginName):
- settings.setValue('/PythonPlugins/' + pluginName, True)
+ settings.setValue("/PythonPlugins/" + pluginName, True)
self.exportPluginsToManager()
msg = "%s" % self.tr("Plugin installed successfully")
else:
- msg = "%s: %s" % (self.tr("Plugin installation failed"), infoString)
+ msg = "{}: {}".format(
+ self.tr("Plugin installation failed"), infoString
+ )
level = Qgis.MessageLevel.Success if success else Qgis.MessageLevel.Critical
iface.pluginManagerInterface().pushMessage(msg, level)
@@ -710,22 +896,47 @@ def processDependencies(self, plugin_id):
to_install, to_upgrade, not_found = find_dependencies(plugin_id)
if to_install or to_upgrade or not_found:
- dlg = QgsPluginDependenciesDialog(plugin_id, to_install, to_upgrade, not_found)
+ dlg = QgsPluginDependenciesDialog(
+ plugin_id, to_install, to_upgrade, not_found
+ )
if dlg.exec() == QgsPluginDependenciesDialog.Accepted:
actions = dlg.actions()
for dependency_plugin_id, action_data in actions.items():
try:
- self.installPlugin(dependency_plugin_id, stable=action_data['use_stable_version'])
- if action_data['action'] == 'install':
- iface.pluginManagerInterface().pushMessage(self.tr("Plugin dependency %s successfully installed") %
- dependency_plugin_id, Qgis.MessageLevel.Success)
+ self.installPlugin(
+ dependency_plugin_id,
+ stable=action_data["use_stable_version"],
+ )
+ if action_data["action"] == "install":
+ iface.pluginManagerInterface().pushMessage(
+ self.tr(
+ "Plugin dependency %s successfully installed"
+ )
+ % dependency_plugin_id,
+ Qgis.MessageLevel.Success,
+ )
else:
- iface.pluginManagerInterface().pushMessage(self.tr("Plugin dependency %s successfully upgraded") %
- dependency_plugin_id, Qgis.MessageLevel.Success)
+ iface.pluginManagerInterface().pushMessage(
+ self.tr(
+ "Plugin dependency %s successfully upgraded"
+ )
+ % dependency_plugin_id,
+ Qgis.MessageLevel.Success,
+ )
except Exception as ex:
- if action_data['action'] == 'install':
- iface.pluginManagerInterface().pushMessage(self.tr("Error installing plugin dependency %s: %s") %
- (dependency_plugin_id, ex), Qgis.MessageLevel.Warning)
+ if action_data["action"] == "install":
+ iface.pluginManagerInterface().pushMessage(
+ self.tr(
+ "Error installing plugin dependency %s: %s"
+ )
+ % (dependency_plugin_id, ex),
+ Qgis.MessageLevel.Warning,
+ )
else:
- iface.pluginManagerInterface().pushMessage(self.tr("Error upgrading plugin dependency %s: %s") %
- (dependency_plugin_id, ex), Qgis.MessageLevel.Warning)
+ iface.pluginManagerInterface().pushMessage(
+ self.tr(
+ "Error upgrading plugin dependency %s: %s"
+ )
+ % (dependency_plugin_id, ex),
+ Qgis.MessageLevel.Warning,
+ )
diff --git a/python/pyplugin_installer/installer_data.py b/python/pyplugin_installer/installer_data.py
index 089d10a872a5..81a830e554d7 100644
--- a/python/pyplugin_installer/installer_data.py
+++ b/python/pyplugin_installer/installer_data.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
Plugin Installer module
@@ -22,15 +21,23 @@
* *
***************************************************************************/
"""
-from typing import (
- Dict,
- Optional,
- Any
-)
-from qgis.PyQt.QtCore import (pyqtSignal, QObject, QCoreApplication, QFile,
- QDir, QDirIterator, QDate, QUrl, QFileInfo,
- QLocale, QByteArray, QT_VERSION_STR)
+from typing import Dict, Optional, Any
+
+from qgis.PyQt.QtCore import (
+ pyqtSignal,
+ QObject,
+ QCoreApplication,
+ QFile,
+ QDir,
+ QDirIterator,
+ QDate,
+ QUrl,
+ QFileInfo,
+ QLocale,
+ QByteArray,
+ QT_VERSION_STR,
+)
from qgis.PyQt.QtXml import QDomDocument
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.core import Qgis, QgsSettings, QgsSettingsTree, QgsNetworkRequestParameters
@@ -42,12 +49,13 @@
import qgis.utils
from qgis.core import QgsNetworkAccessManager, QgsApplication
from qgis.gui import QgsGui
-from qgis.utils import (
- iface,
- plugin_paths,
- HOME_PLUGIN_PATH
+from qgis.utils import iface, plugin_paths, HOME_PLUGIN_PATH
+from .version_compare import (
+ pyQgisVersion,
+ compareVersions,
+ normalizeVersion,
+ isCompatible,
)
-from .version_compare import pyQgisVersion, compareVersions, normalizeVersion, isCompatible
"""
@@ -111,14 +119,24 @@
reposGroup = "app/plugin_repositories"
-officialRepo = (QCoreApplication.translate("QgsPluginInstaller", "QGIS Official Plugin Repository"), "https://plugins.qgis.org/plugins/plugins.xml")
+officialRepo = (
+ QCoreApplication.translate("QgsPluginInstaller", "QGIS Official Plugin Repository"),
+ "https://plugins.qgis.org/plugins/plugins.xml",
+)
# --- common functions ------------------------------------------------------------------- #
def removeDir(path):
result = ""
if not QFile(path).exists():
- result = QCoreApplication.translate("QgsPluginInstaller", "Nothing to remove! Plugin directory doesn't exist:") + "\n" + path
+ result = (
+ QCoreApplication.translate(
+ "QgsPluginInstaller",
+ "Nothing to remove! Plugin directory doesn't exist:",
+ )
+ + "\n"
+ + path
+ )
elif QFile(path).remove(): # if it is only link, just remove it without resolving.
pass
else:
@@ -135,7 +153,17 @@ def removeDir(path):
if QDir().rmpath(item):
pass
if QFile(path).exists():
- result = QCoreApplication.translate("QgsPluginInstaller", "Failed to remove the directory:") + "\n" + path + "\n" + QCoreApplication.translate("QgsPluginInstaller", "Check permissions or remove it manually")
+ result = (
+ QCoreApplication.translate(
+ "QgsPluginInstaller", "Failed to remove the directory:"
+ )
+ + "\n"
+ + path
+ + "\n"
+ + QCoreApplication.translate(
+ "QgsPluginInstaller", "Check permissions or remove it manually"
+ )
+ )
# restore plugin directory if removed by QDir().rmpath()
pluginDir = HOME_PLUGIN_PATH
if not QDir(pluginDir).exists():
@@ -147,6 +175,7 @@ class Relay(QObject):
"""
Relay object for transmitting signals from QPHttp with adding the repoName information
"""
+
anythingChanged = pyqtSignal(str, int, int)
def __init__(self, key):
@@ -184,71 +213,97 @@ class Repositories(QObject):
def __init__(self):
QObject.__init__(self)
- self.mRepositories: Dict[str, Dict[str, Any]] = {}
- self.httpId = {} # {httpId : repoName}
+ self.mRepositories: dict[str, dict[str, Any]] = {}
+ self.httpId = {} # {httpId : repoName}
self.mInspectionFilter: Optional[str] = None
- def all(self) -> Dict[str, Dict[str, Any]]:
- """ return dict of all repositories """
+ def all(self) -> dict[str, dict[str, Any]]:
+ """return dict of all repositories"""
return self.mRepositories
- def allEnabled(self) -> Dict[str, Dict[str, Any]]:
- """ return dict of all enabled and valid repositories """
+ def allEnabled(self) -> dict[str, dict[str, Any]]:
+ """return dict of all enabled and valid repositories"""
if self.mInspectionFilter:
return {self.mInspectionFilter: self.mRepositories[self.mInspectionFilter]}
- return {k: v for k, v in self.mRepositories.items() if v['enabled'] and v['valid']}
+ return {
+ k: v for k, v in self.mRepositories.items() if v["enabled"] and v["valid"]
+ }
- def allUnavailable(self) -> Dict[str, Dict[str, Any]]:
- """ return dict of all unavailable repositories """
+ def allUnavailable(self) -> dict[str, dict[str, Any]]:
+ """return dict of all unavailable repositories"""
repos = {}
if self.mInspectionFilter:
# return the inspected repo if unavailable, otherwise empty dict
- if self.mRepositories[self.mInspectionFilter]["state"] == Repositories.STATE_UNAVAILABLE:
- repos[self.mInspectionFilter] = self.mRepositories[self.mInspectionFilter]
+ if (
+ self.mRepositories[self.mInspectionFilter]["state"]
+ == Repositories.STATE_UNAVAILABLE
+ ):
+ repos[self.mInspectionFilter] = self.mRepositories[
+ self.mInspectionFilter
+ ]
return repos
- return {k: v for k, v in self.mRepositories.items() if v['enabled'] and v['valid'] and v['state'] == Repositories.STATE_UNAVAILABLE}
+ return {
+ k: v
+ for k, v in self.mRepositories.items()
+ if v["enabled"]
+ and v["valid"]
+ and v["state"] == Repositories.STATE_UNAVAILABLE
+ }
def urlParams(self) -> str:
- """ return GET parameters to be added to every request """
+ """return GET parameters to be added to every request"""
# Strip down the point release segment from the version string
- return "?qgis={}".format(re.sub(r'\.\d*$', '', pyQgisVersion()))
+ return "?qgis={}".format(re.sub(r"\.\d*$", "", pyQgisVersion()))
def setRepositoryData(self, reposName: str, key: str, value):
- """ write data to the mRepositories dict """
+ """write data to the mRepositories dict"""
self.mRepositories[reposName][key] = value
def remove(self, reposName: str):
- """ remove given item from the mRepositories dict """
+ """remove given item from the mRepositories dict"""
del self.mRepositories[reposName]
def rename(self, oldName: str, newName: str):
- """ rename repository key """
+ """rename repository key"""
if oldName == newName:
return
self.mRepositories[newName] = self.mRepositories[oldName]
del self.mRepositories[oldName]
def checkingOnStart(self) -> bool:
- """ return true if checking for news and updates is enabled """
- return QgsSettingsTree.node("plugin-manager").childSetting('automatically-check-for-updates').value()
+ """return true if checking for news and updates is enabled"""
+ return (
+ QgsSettingsTree.node("plugin-manager")
+ .childSetting("automatically-check-for-updates")
+ .value()
+ )
def setCheckingOnStart(self, state: bool):
- """ set state of checking for news and updates """
- QgsSettingsTree.node("plugin-manager").childSetting('automatically-check-for-updates').setValue(state)
+ """set state of checking for news and updates"""
+ QgsSettingsTree.node("plugin-manager").childSetting(
+ "automatically-check-for-updates"
+ ).setValue(state)
def saveCheckingOnStartLastDate(self):
- """ set today's date as the day of last checking """
- QgsSettingsTree.node("plugin-manager").childSetting('check-on-start-last-date').setValue(QDate.currentDate())
+ """set today's date as the day of last checking"""
+ QgsSettingsTree.node("plugin-manager").childSetting(
+ "check-on-start-last-date"
+ ).setValue(QDate.currentDate())
def timeForChecking(self) -> bool:
- """ determine whether it's the time for checking for news and updates now """
+ """determine whether it's the time for checking for news and updates now"""
settings = QgsSettings()
try:
# QgsSettings may contain ivalid value...
- interval = QgsSettingsTree.node("plugin-manager").childSetting('check-on-start-last-date').valueAs(type=QDate).daysTo(QDate.currentDate())
+ interval = (
+ QgsSettingsTree.node("plugin-manager")
+ .childSetting("check-on-start-last-date")
+ .valueAs(type=QDate)
+ .daysTo(QDate.currentDate())
+ )
except:
interval = 0
if interval >= Repositories.CHECK_ON_START_INTERVAL:
@@ -257,7 +312,7 @@ def timeForChecking(self) -> bool:
return False
def load(self):
- """ populate the mRepositories dict"""
+ """populate the mRepositories dict"""
self.mRepositories = {}
settings = QgsSettings()
settings.beginGroup(reposGroup)
@@ -273,17 +328,29 @@ def load(self):
for key in settings.childGroups():
self.mRepositories[key] = {}
self.mRepositories[key]["url"] = settings.value(key + "/url", "", type=str)
- self.mRepositories[key]["authcfg"] = settings.value(key + "/authcfg", "", type=str)
- self.mRepositories[key]["enabled"] = settings.value(key + "/enabled", True, type=bool)
- self.mRepositories[key]["valid"] = settings.value(key + "/valid", True, type=bool)
+ self.mRepositories[key]["authcfg"] = settings.value(
+ key + "/authcfg", "", type=str
+ )
+ self.mRepositories[key]["enabled"] = settings.value(
+ key + "/enabled", True, type=bool
+ )
+ self.mRepositories[key]["valid"] = settings.value(
+ key + "/valid", True, type=bool
+ )
self.mRepositories[key]["Relay"] = Relay(key)
self.mRepositories[key]["xmlData"] = None
self.mRepositories[key]["state"] = Repositories.STATE_DISABLED
self.mRepositories[key]["error"] = ""
settings.endGroup()
- def requestFetching(self, key: str, url: Optional[QUrl] = None, redirectionCounter=0, force_reload: bool = False):
- """ start fetching the repository given by key """
+ def requestFetching(
+ self,
+ key: str,
+ url: Optional[QUrl] = None,
+ redirectionCounter=0,
+ force_reload: bool = False,
+ ):
+ """start fetching the repository given by key"""
self.mRepositories[key]["state"] = Repositories.STATE_LOADING
if not url:
url = QUrl(self.mRepositories[key]["url"] + self.urlParams())
@@ -291,55 +358,98 @@ def requestFetching(self, key: str, url: Optional[QUrl] = None, redirectionCount
# url.addQueryItem('qgis', '.'.join([str(int(s)) for s in [v[0], v[1:3]]]) ) # don't include the bugfix version!
self.mRepositories[key]["QRequest"] = QNetworkRequest(url)
- self.mRepositories[key]["QRequest"].setAttribute(QNetworkRequest.Attribute(QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass), "Relay")
- self.mRepositories[key]["QRequest"].setAttribute(QNetworkRequest.Attribute.RedirectPolicyAttribute, QNetworkRequest.RedirectPolicy.NoLessSafeRedirectPolicy)
+ self.mRepositories[key]["QRequest"].setAttribute(
+ QNetworkRequest.Attribute(
+ QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass
+ ),
+ "Relay",
+ )
+ self.mRepositories[key]["QRequest"].setAttribute(
+ QNetworkRequest.Attribute.RedirectPolicyAttribute,
+ QNetworkRequest.RedirectPolicy.NoLessSafeRedirectPolicy,
+ )
if force_reload:
- self.mRepositories[key]["QRequest"].setAttribute(QNetworkRequest.Attribute.CacheLoadControlAttribute, QNetworkRequest.CacheLoadControl.AlwaysNetwork)
+ self.mRepositories[key]["QRequest"].setAttribute(
+ QNetworkRequest.Attribute.CacheLoadControlAttribute,
+ QNetworkRequest.CacheLoadControl.AlwaysNetwork,
+ )
authcfg = self.mRepositories[key]["authcfg"]
if authcfg and isinstance(authcfg, str):
if not QgsApplication.authManager().updateNetworkRequest(
- self.mRepositories[key]["QRequest"], authcfg.strip()):
+ self.mRepositories[key]["QRequest"], authcfg.strip()
+ ):
msg = QCoreApplication.translate(
"QgsPluginInstaller",
"Update of network request with authentication "
- "credentials FAILED for configuration '{0}'").format(authcfg)
- iface.pluginManagerInterface().pushMessage(msg, Qgis.MessageLevel.Warning)
+ "credentials FAILED for configuration '{0}'",
+ ).format(authcfg)
+ iface.pluginManagerInterface().pushMessage(
+ msg, Qgis.MessageLevel.Warning
+ )
self.mRepositories[key]["QRequest"] = None
return
- self.mRepositories[key]["QRequest"].setAttribute(QNetworkRequest.Attribute.User, key)
- self.mRepositories[key]["xmlData"] = QgsNetworkAccessManager.instance().get(self.mRepositories[key]["QRequest"])
- self.mRepositories[key]["xmlData"].setProperty('reposName', key)
- self.mRepositories[key]["xmlData"].setProperty('redirectionCounter', redirectionCounter)
- self.mRepositories[key]["xmlData"].downloadProgress.connect(self.mRepositories[key]["Relay"].dataReadProgress)
- self.mRepositories[key]["xmlDataFinished"] = self.mRepositories[key]["xmlData"].finished.connect(self.xmlDownloaded)
+ self.mRepositories[key]["QRequest"].setAttribute(
+ QNetworkRequest.Attribute.User, key
+ )
+ self.mRepositories[key]["xmlData"] = QgsNetworkAccessManager.instance().get(
+ self.mRepositories[key]["QRequest"]
+ )
+ self.mRepositories[key]["xmlData"].setProperty("reposName", key)
+ self.mRepositories[key]["xmlData"].setProperty(
+ "redirectionCounter", redirectionCounter
+ )
+ self.mRepositories[key]["xmlData"].downloadProgress.connect(
+ self.mRepositories[key]["Relay"].dataReadProgress
+ )
+ self.mRepositories[key]["xmlDataFinished"] = self.mRepositories[key][
+ "xmlData"
+ ].finished.connect(self.xmlDownloaded)
def fetchingInProgress(self) -> bool:
- """ return True if fetching repositories is still in progress """
- return any(v['state'] == Repositories.STATE_LOADING for v in self.mRepositories.values())
+ """return True if fetching repositories is still in progress"""
+ return any(
+ v["state"] == Repositories.STATE_LOADING
+ for v in self.mRepositories.values()
+ )
def killConnection(self, key: str):
- """ kill the fetching on demand """
- if self.mRepositories[key]["state"] == Repositories.STATE_LOADING and self.mRepositories[key]["xmlData"] and self.mRepositories[key]["xmlData"].isRunning():
- self.mRepositories[key]["xmlData"].finished.disconnect(self.mRepositories[key]["xmlDataFinished"])
+ """kill the fetching on demand"""
+ if (
+ self.mRepositories[key]["state"] == Repositories.STATE_LOADING
+ and self.mRepositories[key]["xmlData"]
+ and self.mRepositories[key]["xmlData"].isRunning()
+ ):
+ self.mRepositories[key]["xmlData"].finished.disconnect(
+ self.mRepositories[key]["xmlDataFinished"]
+ )
self.mRepositories[key]["xmlData"].abort()
def xmlDownloaded(self):
- """ populate the plugins object with the fetched data """
+ """populate the plugins object with the fetched data"""
reply = self.sender()
- reposName = reply.property('reposName')
+ reposName = reply.property("reposName")
if reply.error() != QNetworkReply.NetworkError.NoError: # fetching failed
self.mRepositories[reposName]["state"] = Repositories.STATE_UNAVAILABLE
self.mRepositories[reposName]["error"] = reply.errorString()
if reply.error() == QNetworkReply.NetworkError.OperationCanceledError:
- self.mRepositories[reposName]["error"] += "\n\n" + QCoreApplication.translate("QgsPluginInstaller", "If you haven't canceled the download manually, it was most likely caused by a timeout. In this case consider increasing the connection timeout value in QGIS options window.")
+ self.mRepositories[reposName][
+ "error"
+ ] += "\n\n" + QCoreApplication.translate(
+ "QgsPluginInstaller",
+ "If you haven't canceled the download manually, it was most likely caused by a timeout. In this case consider increasing the connection timeout value in QGIS options window.",
+ )
elif reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) == 301:
- redirectionUrl = reply.attribute(QNetworkRequest.Attribute.RedirectionTargetAttribute)
+ redirectionUrl = reply.attribute(
+ QNetworkRequest.Attribute.RedirectionTargetAttribute
+ )
if redirectionUrl.isRelative():
redirectionUrl = reply.url().resolved(redirectionUrl)
- redirectionCounter = reply.property('redirectionCounter') + 1
+ redirectionCounter = reply.property("redirectionCounter") + 1
if redirectionCounter > 4:
self.mRepositories[reposName]["state"] = Repositories.STATE_UNAVAILABLE
- self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Too many redirections")
+ self.mRepositories[reposName]["error"] = QCoreApplication.translate(
+ "QgsPluginInstaller", "Too many redirections"
+ )
else:
# Fire a new request and exit immediately in order to quietly destroy the old one
self.requestFetching(reposName, redirectionUrl, redirectionCounter)
@@ -355,62 +465,165 @@ def xmlDownloaded(self):
if plugins_tag.size():
pluginNodes = reposXML.elementsByTagName("pyqgis_plugin")
for i in range(pluginNodes.size()):
- fileName = pluginNodes.item(i).firstChildElement("file_name").text().strip()
+ fileName = (
+ pluginNodes.item(i)
+ .firstChildElement("file_name")
+ .text()
+ .strip()
+ )
if not fileName:
- fileName = QFileInfo(pluginNodes.item(i).firstChildElement("download_url").text().strip().split("?")[0]).fileName()
+ fileName = QFileInfo(
+ pluginNodes.item(i)
+ .firstChildElement("download_url")
+ .text()
+ .strip()
+ .split("?")[0]
+ ).fileName()
name = fileName.partition(".")[0]
experimental = False
- if pluginNodes.item(i).firstChildElement("experimental").text().strip().upper() in ["TRUE", "YES"]:
+ if pluginNodes.item(i).firstChildElement(
+ "experimental"
+ ).text().strip().upper() in ["TRUE", "YES"]:
experimental = True
deprecated = False
- if pluginNodes.item(i).firstChildElement("deprecated").text().strip().upper() in ["TRUE", "YES"]:
+ if pluginNodes.item(i).firstChildElement(
+ "deprecated"
+ ).text().strip().upper() in ["TRUE", "YES"]:
deprecated = True
trusted = False
- if pluginNodes.item(i).firstChildElement("trusted").text().strip().upper() in ["TRUE", "YES"]:
+ if pluginNodes.item(i).firstChildElement(
+ "trusted"
+ ).text().strip().upper() in ["TRUE", "YES"]:
trusted = True
icon = pluginNodes.item(i).firstChildElement("icon").text().strip()
if icon and not icon.startswith("http"):
url = QUrl(self.mRepositories[reposName]["url"])
- if url.scheme() in ('http', 'https'):
- icon = "{}://{}/{}".format(url.scheme(), url.host(), icon)
+ if url.scheme() in ("http", "https"):
+ icon = f"{url.scheme()}://{url.host()}/{icon}"
if pluginNodes.item(i).toElement().hasAttribute("plugin_id"):
- plugin_id = pluginNodes.item(i).toElement().attribute("plugin_id")
+ plugin_id = (
+ pluginNodes.item(i).toElement().attribute("plugin_id")
+ )
else:
plugin_id = None
version = pluginNodes.item(i).toElement().attribute("version")
- download_url = pluginNodes.item(i).firstChildElement("download_url").text().strip()
+ download_url = (
+ pluginNodes.item(i)
+ .firstChildElement("download_url")
+ .text()
+ .strip()
+ )
plugin = {
"id": name,
"plugin_id": plugin_id,
"name": pluginNodes.item(i).toElement().attribute("name"),
"version_available": version,
- "version_available_stable": normalizeVersion(version) if not experimental else "",
- "version_available_experimental": normalizeVersion(version) if experimental else "",
- "description": pluginNodes.item(i).firstChildElement("description").text().strip(),
- "about": pluginNodes.item(i).firstChildElement("about").text().strip(),
- "author_name": pluginNodes.item(i).firstChildElement("author_name").text().strip(),
- "homepage": pluginNodes.item(i).firstChildElement("homepage").text().strip(),
+ "version_available_stable": (
+ normalizeVersion(version) if not experimental else ""
+ ),
+ "version_available_experimental": (
+ normalizeVersion(version) if experimental else ""
+ ),
+ "description": pluginNodes.item(i)
+ .firstChildElement("description")
+ .text()
+ .strip(),
+ "about": pluginNodes.item(i)
+ .firstChildElement("about")
+ .text()
+ .strip(),
+ "author_name": pluginNodes.item(i)
+ .firstChildElement("author_name")
+ .text()
+ .strip(),
+ "homepage": pluginNodes.item(i)
+ .firstChildElement("homepage")
+ .text()
+ .strip(),
"download_url": download_url,
"download_url_stable": download_url if not experimental else "",
- "download_url_experimental": download_url if experimental else "",
- "category": pluginNodes.item(i).firstChildElement("category").text().strip(),
- "tags": pluginNodes.item(i).firstChildElement("tags").text().strip(),
- "changelog": pluginNodes.item(i).firstChildElement("changelog").text().strip(),
- "author_email": pluginNodes.item(i).firstChildElement("author_email").text().strip(),
- "tracker": pluginNodes.item(i).firstChildElement("tracker").text().strip(),
- "code_repository": pluginNodes.item(i).firstChildElement("repository").text().strip(),
- "downloads": pluginNodes.item(i).firstChildElement("downloads").text().strip(),
- "average_vote": pluginNodes.item(i).firstChildElement("average_vote").text().strip(),
- "rating_votes": pluginNodes.item(i).firstChildElement("rating_votes").text().strip(),
- "create_date": pluginNodes.item(i).firstChildElement("create_date").text().strip(),
- "update_date": pluginNodes.item(i).firstChildElement("update_date").text().strip(),
- "create_date_stable": pluginNodes.item(i).firstChildElement("create_date").text().strip() if not experimental else "",
- "update_date_stable": pluginNodes.item(i).firstChildElement("update_date").text().strip() if not experimental else "",
- "create_date_experimental": pluginNodes.item(i).firstChildElement("create_date").text().strip() if experimental else "",
- "update_date_experimental": pluginNodes.item(i).firstChildElement("update_date").text().strip() if experimental else "",
+ "download_url_experimental": (
+ download_url if experimental else ""
+ ),
+ "category": pluginNodes.item(i)
+ .firstChildElement("category")
+ .text()
+ .strip(),
+ "tags": pluginNodes.item(i)
+ .firstChildElement("tags")
+ .text()
+ .strip(),
+ "changelog": pluginNodes.item(i)
+ .firstChildElement("changelog")
+ .text()
+ .strip(),
+ "author_email": pluginNodes.item(i)
+ .firstChildElement("author_email")
+ .text()
+ .strip(),
+ "tracker": pluginNodes.item(i)
+ .firstChildElement("tracker")
+ .text()
+ .strip(),
+ "code_repository": pluginNodes.item(i)
+ .firstChildElement("repository")
+ .text()
+ .strip(),
+ "downloads": pluginNodes.item(i)
+ .firstChildElement("downloads")
+ .text()
+ .strip(),
+ "average_vote": pluginNodes.item(i)
+ .firstChildElement("average_vote")
+ .text()
+ .strip(),
+ "rating_votes": pluginNodes.item(i)
+ .firstChildElement("rating_votes")
+ .text()
+ .strip(),
+ "create_date": pluginNodes.item(i)
+ .firstChildElement("create_date")
+ .text()
+ .strip(),
+ "update_date": pluginNodes.item(i)
+ .firstChildElement("update_date")
+ .text()
+ .strip(),
+ "create_date_stable": (
+ pluginNodes.item(i)
+ .firstChildElement("create_date")
+ .text()
+ .strip()
+ if not experimental
+ else ""
+ ),
+ "update_date_stable": (
+ pluginNodes.item(i)
+ .firstChildElement("update_date")
+ .text()
+ .strip()
+ if not experimental
+ else ""
+ ),
+ "create_date_experimental": (
+ pluginNodes.item(i)
+ .firstChildElement("create_date")
+ .text()
+ .strip()
+ if experimental
+ else ""
+ ),
+ "update_date_experimental": (
+ pluginNodes.item(i)
+ .firstChildElement("update_date")
+ .text()
+ .strip()
+ if experimental
+ else ""
+ ),
"icon": icon,
"experimental": experimental,
"deprecated": deprecated,
@@ -426,29 +639,58 @@ def xmlDownloaded(self):
"zip_repository": reposName,
"library": "",
"readonly": False,
- "plugin_dependencies": pluginNodes.item(i).firstChildElement("plugin_dependencies").text().strip(),
+ "plugin_dependencies": pluginNodes.item(i)
+ .firstChildElement("plugin_dependencies")
+ .text()
+ .strip(),
}
- qgisMinimumVersion = pluginNodes.item(i).firstChildElement("qgis_minimum_version").text().strip()
+ qgisMinimumVersion = (
+ pluginNodes.item(i)
+ .firstChildElement("qgis_minimum_version")
+ .text()
+ .strip()
+ )
if not qgisMinimumVersion:
qgisMinimumVersion = "2"
- qgisMaximumVersion = pluginNodes.item(i).firstChildElement("qgis_maximum_version").text().strip()
+ qgisMaximumVersion = (
+ pluginNodes.item(i)
+ .firstChildElement("qgis_maximum_version")
+ .text()
+ .strip()
+ )
if not qgisMaximumVersion:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
# if compatible, add the plugin to the list
- if not pluginNodes.item(i).firstChildElement("disabled").text().strip().upper() in ["TRUE", "YES"]:
- if isCompatible(pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion):
+ if not pluginNodes.item(i).firstChildElement(
+ "disabled"
+ ).text().strip().upper() in ["TRUE", "YES"]:
+ if isCompatible(
+ pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion
+ ):
# add the plugin to the cache
plugins.addFromRepository(plugin)
self.mRepositories[reposName]["state"] = Repositories.STATE_LOADED
else:
# no plugin metadata found
self.mRepositories[reposName]["state"] = Repositories.STATE_UNAVAILABLE
- if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) == 200:
- self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Server response is 200 OK, but doesn't contain plugin metadata. This is most likely caused by a proxy or a wrong repository URL. You can configure proxy settings in QGIS options.")
+ if (
+ reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute)
+ == 200
+ ):
+ self.mRepositories[reposName]["error"] = QCoreApplication.translate(
+ "QgsPluginInstaller",
+ "Server response is 200 OK, but doesn't contain plugin metadata. This is most likely caused by a proxy or a wrong repository URL. You can configure proxy settings in QGIS options.",
+ )
else:
- self.mRepositories[reposName]["error"] = QCoreApplication.translate("QgsPluginInstaller", "Status code:") + " {} {}".format(
- reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute),
- reply.attribute(QNetworkRequest.Attribute.HttpReasonPhraseAttribute)
+ self.mRepositories[reposName]["error"] = QCoreApplication.translate(
+ "QgsPluginInstaller", "Status code:"
+ ) + " {} {}".format(
+ reply.attribute(
+ QNetworkRequest.Attribute.HttpStatusCodeAttribute
+ ),
+ reply.attribute(
+ QNetworkRequest.Attribute.HttpReasonPhraseAttribute
+ ),
)
self.repositoryFetched.emit(reposName)
@@ -460,35 +702,37 @@ def xmlDownloaded(self):
reply.deleteLater()
def inspectionFilter(self) -> Optional[str]:
- """ return inspection filter (only one repository to be fetched) """
+ """return inspection filter (only one repository to be fetched)"""
return self.mInspectionFilter
def setInspectionFilter(self, key: Optional[str] = None):
- """ temporarily disable all repositories but this for inspection """
+ """temporarily disable all repositories but this for inspection"""
self.mInspectionFilter = key
# --- class Plugins ---------------------------------------------------------------------- #
class Plugins(QObject):
+ """A dict-like class for handling plugins data"""
- """ A dict-like class for handling plugins data """
# ----------------------------------------- #
def __init__(self):
QObject.__init__(self)
- self.mPlugins = {} # the dict of plugins (dicts)
- self.repoCache = {} # the dict of lists of plugins (dicts)
- self.localCache = {} # the dict of plugins (dicts)
- self.obsoletePlugins = [] # the list of outdated 'user' plugins masking newer 'system' ones
+ self.mPlugins = {} # the dict of plugins (dicts)
+ self.repoCache = {} # the dict of lists of plugins (dicts)
+ self.localCache = {} # the dict of plugins (dicts)
+ self.obsoletePlugins = (
+ []
+ ) # the list of outdated 'user' plugins masking newer 'system' ones
# ----------------------------------------- #
def all(self):
- """ return all plugins """
+ """return all plugins"""
return self.mPlugins
# ----------------------------------------- #
def allUpgradeable(self):
- """ return all upgradeable plugins """
+ """return all upgradeable plugins"""
result = {}
for i in self.mPlugins:
if self.mPlugins[i]["status"] == "upgradeable":
@@ -497,7 +741,7 @@ def allUpgradeable(self):
# ----------------------------------------- #
def keyByUrl(self, name):
- """ return plugin key by given url """
+ """return plugin key by given url"""
plugins = [i for i in self.mPlugins if self.mPlugins[i]["download_url"] == name]
if plugins:
return plugins[0]
@@ -505,12 +749,12 @@ def keyByUrl(self, name):
# ----------------------------------------- #
def clearRepoCache(self):
- """ clears the repo cache before re-fetching repositories """
+ """clears the repo cache before re-fetching repositories"""
self.repoCache = {}
# ----------------------------------------- #
def addFromRepository(self, plugin):
- """ add given plugin to the repoCache """
+ """add given plugin to the repoCache"""
repo = plugin["zip_repository"]
try:
self.repoCache[repo] += [plugin]
@@ -519,44 +763,45 @@ def addFromRepository(self, plugin):
# ----------------------------------------- #
def removeInstalledPlugin(self, key):
- """ remove given plugin from the localCache """
+ """remove given plugin from the localCache"""
if key in self.localCache:
del self.localCache[key]
# ----------------------------------------- #
def removeRepository(self, repo: str):
- """ remove whole repository from the repoCache """
+ """remove whole repository from the repoCache"""
if repo in self.repoCache:
del self.repoCache[repo]
# ----------------------------------------- #
def getInstalledPlugin(self, key, path, readOnly):
- """ get the metadata of an installed plugin """
+ """get the metadata of an installed plugin"""
+
def metadataParser(fct):
- """ plugin metadata parser reimplemented from qgis.utils
- for better control of which module is examined
- in case there is an installed plugin masking a core one """
+ """plugin metadata parser reimplemented from qgis.utils
+ for better control of which module is examined
+ in case there is an installed plugin masking a core one"""
global errorDetails
cp = configparser.ConfigParser()
try:
with codecs.open(metadataFile, "r", "utf8") as f:
cp.read_file(f)
- return cp.get('general', fct)
+ return cp.get("general", fct)
except Exception as e:
if not errorDetails:
errorDetails = e.args[0] # set to the first problem
return ""
def pluginMetadata(fct):
- """ calls metadataParser for current l10n.
- If failed, fallbacks to the standard metadata """
- overrideLocale = QgsSettings().value('locale/overrideFlag', False, bool)
+ """calls metadataParser for current l10n.
+ If failed, fallbacks to the standard metadata"""
+ overrideLocale = QgsSettings().value("locale/overrideFlag", False, bool)
if not overrideLocale:
locale = QLocale.system().name()
else:
- locale = QgsSettings().value('locale/userLocale', '')
+ locale = QgsSettings().value("locale/userLocale", "")
if locale and fct in translatableAttributes:
- value = metadataParser("{}[{}]".format(fct, locale))
+ value = metadataParser(f"{fct}[{locale}]")
if value:
return value
value = metadataParser("{}[{}]".format(fct, locale.split("_")[0]))
@@ -573,19 +818,27 @@ def pluginMetadata(fct):
errorDetails = ""
version = None
- if not os.path.exists(os.path.join(path, '__init__.py')):
+ if not os.path.exists(os.path.join(path, "__init__.py")):
error = "broken"
- errorDetails = QCoreApplication.translate("QgsPluginInstaller", "Missing __init__.py")
+ errorDetails = QCoreApplication.translate(
+ "QgsPluginInstaller", "Missing __init__.py"
+ )
- metadataFile = os.path.join(path, 'metadata.txt')
+ metadataFile = os.path.join(path, "metadata.txt")
if os.path.exists(metadataFile):
version = normalizeVersion(pluginMetadata("version"))
- qt_version = int(QT_VERSION_STR.split('.')[0])
+ qt_version = int(QT_VERSION_STR.split(".")[0])
supports_qt6 = pluginMetadata("supportsQt6").strip().upper() in ("TRUE", "YES")
- if qt_version == 6 and not supports_qt6 and "QGIS_DISABLE_SUPPORTS_QT6_CHECK" not in os.environ:
+ if (
+ qt_version == 6
+ and not supports_qt6
+ and "QGIS_DISABLE_SUPPORTS_QT6_CHECK" not in os.environ
+ ):
error = "incompatible"
- errorDetails = QCoreApplication.translate("QgsPluginInstaller", "Plugin does not support Qt6 versions of QGIS")
+ errorDetails = QCoreApplication.translate(
+ "QgsPluginInstaller", "Plugin does not support Qt6 versions of QGIS"
+ )
elif version:
qgisMinimumVersion = pluginMetadata("qgisMinimumVersion").strip()
if not qgisMinimumVersion:
@@ -594,16 +847,22 @@ def pluginMetadata(fct):
if not qgisMaximumVersion:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
# if compatible, add the plugin to the list
- if not isCompatible(pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion):
+ if not isCompatible(
+ pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion
+ ):
error = "incompatible"
- errorDetails = "{} - {}".format(qgisMinimumVersion, qgisMaximumVersion)
+ errorDetails = f"{qgisMinimumVersion} - {qgisMaximumVersion}"
elif not os.path.exists(metadataFile):
error = "broken"
- errorDetails = QCoreApplication.translate("QgsPluginInstaller", "Missing metadata file")
+ errorDetails = QCoreApplication.translate(
+ "QgsPluginInstaller", "Missing metadata file"
+ )
else:
error = "broken"
e = errorDetails
- errorDetails = QCoreApplication.translate("QgsPluginInstaller", "Error reading metadata")
+ errorDetails = QCoreApplication.translate(
+ "QgsPluginInstaller", "Error reading metadata"
+ )
if e:
errorDetails += ": " + e
@@ -644,14 +903,16 @@ def pluginMetadata(fct):
"version_installed": version,
"library": path,
"pythonic": True,
- "experimental": pluginMetadata("experimental").strip().upper() in ["TRUE", "YES"],
- "deprecated": pluginMetadata("deprecated").strip().upper() in ["TRUE", "YES"],
+ "experimental": pluginMetadata("experimental").strip().upper()
+ in ["TRUE", "YES"],
+ "deprecated": pluginMetadata("deprecated").strip().upper()
+ in ["TRUE", "YES"],
"trusted": False,
"version_available": "",
"version_available_stable": "",
"version_available_experimental": "",
"zip_repository": "",
- "download_url": path, # warning: local path as url!
+ "download_url": path, # warning: local path as url!
"download_url_stable": "",
"download_url_experimental": "",
"filename": "",
@@ -664,7 +925,7 @@ def pluginMetadata(fct):
"update_date_stable": pluginMetadata("update_date_stable"),
"create_date_experimental": pluginMetadata("create_date_experimental"),
"update_date_experimental": pluginMetadata("update_date_experimental"),
- "available": False, # Will be overwritten, if any available version found.
+ "available": False, # Will be overwritten, if any available version found.
"installed": True,
"status": "orphan", # Will be overwritten, if any available version found.
"status_exp": "orphan", # Will be overwritten, if any available version found.
@@ -677,7 +938,7 @@ def pluginMetadata(fct):
# ----------------------------------------- #
def getAllInstalled(self):
- """ Build the localCache """
+ """Build the localCache"""
self.localCache = {}
# reversed list of the plugin paths: first system plugins -> then user plugins -> finally custom path(s)
@@ -685,7 +946,9 @@ def getAllInstalled(self):
pluginPaths.reverse()
for pluginsPath in pluginPaths:
- isTheSystemDir = (pluginPaths.index(pluginsPath) == 0) # The current dir is the system plugins dir
+ isTheSystemDir = (
+ pluginPaths.index(pluginsPath) == 0
+ ) # The current dir is the system plugins dir
if isTheSystemDir:
# temporarily add the system path as the first element to force loading the readonly plugins, even if masked by user ones.
sys.path = [pluginsPath] + sys.path
@@ -696,10 +959,19 @@ def getAllInstalled(self):
if key not in [".", ".."]:
path = QDir.toNativeSeparators(pluginsPath + "/" + key)
# readOnly = not QFileInfo(pluginsPath).isWritable() # On windows testing the writable status isn't reliable.
- readOnly = isTheSystemDir # Assume only the system plugins are not writable.
+ readOnly = isTheSystemDir # Assume only the system plugins are not writable.
# failedToLoad = settings.value("/PythonPlugins/watchDog/" + key) is not None
- plugin = self.getInstalledPlugin(key, path=path, readOnly=readOnly)
- if key in list(self.localCache.keys()) and compareVersions(self.localCache[key]["version_installed"], plugin["version_installed"]) == 1:
+ plugin = self.getInstalledPlugin(
+ key, path=path, readOnly=readOnly
+ )
+ if (
+ key in list(self.localCache.keys())
+ and compareVersions(
+ self.localCache[key]["version_installed"],
+ plugin["version_installed"],
+ )
+ == 1
+ ):
# An obsolete plugin in the "user" location is masking a newer one in the "system" location!
self.obsoletePlugins += [key]
self.localCache[key] = plugin
@@ -713,29 +985,45 @@ def getAllInstalled(self):
# ----------------------------------------- #
def rebuild(self):
- """ build or rebuild the mPlugins from the caches """
+ """build or rebuild the mPlugins from the caches"""
self.mPlugins = {}
for i in list(self.localCache.keys()):
self.mPlugins[i] = self.localCache[i].copy()
- allowExperimental = QgsSettingsTree.node("plugin-manager").childSetting("allow-experimental").value()
- allowDeprecated = QgsSettingsTree.node("plugin-manager").childSetting("allow-deprecated").value()
+ allowExperimental = (
+ QgsSettingsTree.node("plugin-manager")
+ .childSetting("allow-experimental")
+ .value()
+ )
+ allowDeprecated = (
+ QgsSettingsTree.node("plugin-manager")
+ .childSetting("allow-deprecated")
+ .value()
+ )
for i in list(self.repoCache.values()):
for j in i:
plugin = j.copy() # do not update repoCache elements!
key = plugin["id"]
# check if the plugin is allowed and if there isn't any better one added already.
- if (allowExperimental or not plugin["experimental"]) \
- and (allowDeprecated or not plugin["deprecated"]) \
- and not (
- key in self.mPlugins and self.mPlugins[key]["version_available"]
- and compareVersions(self.mPlugins[key]["version_available"], plugin["version_available"]) < 2
- and self.mPlugins[key]["experimental"] and not plugin["experimental"]
+ if (
+ (allowExperimental or not plugin["experimental"])
+ and (allowDeprecated or not plugin["deprecated"])
+ and not (
+ key in self.mPlugins
+ and self.mPlugins[key]["version_available"]
+ and compareVersions(
+ self.mPlugins[key]["version_available"],
+ plugin["version_available"],
+ )
+ < 2
+ and self.mPlugins[key]["experimental"]
+ and not plugin["experimental"]
+ )
):
# The mPlugins dict contains now locally installed plugins.
# Now, add the available one if not present yet or update it if present already.
if key not in self.mPlugins:
- self.mPlugins[key] = plugin # just add a new plugin
+ self.mPlugins[key] = plugin # just add a new plugin
else:
# update local plugin with remote metadata
# description, about, icon: only use remote data if local one not available. Prefer local version because of i18n.
@@ -746,18 +1034,56 @@ def rebuild(self):
if not self.mPlugins[key][attrib] and plugin[attrib]:
self.mPlugins[key][attrib] = plugin[attrib]
# other remote metadata is preferred:
- for attrib in ["name", "plugin_id", "description", "about", "category", "tags", "changelog", "author_name", "author_email", "homepage",
- "tracker", "code_repository", "experimental", "deprecated", "version_available", "zip_repository",
- "download_url", "filename", "downloads", "average_vote", "rating_votes", "trusted", "plugin_dependencies",
- "version_available_stable", "version_available_experimental", "download_url_stable", "download_url_experimental",
- "create_date", "update_date", "create_date_stable", "update_date_stable", "create_date_experimental", "update_date_experimental"]:
- if attrib not in translatableAttributes or attrib == "name": # include name!
+ for attrib in [
+ "name",
+ "plugin_id",
+ "description",
+ "about",
+ "category",
+ "tags",
+ "changelog",
+ "author_name",
+ "author_email",
+ "homepage",
+ "tracker",
+ "code_repository",
+ "experimental",
+ "deprecated",
+ "version_available",
+ "zip_repository",
+ "download_url",
+ "filename",
+ "downloads",
+ "average_vote",
+ "rating_votes",
+ "trusted",
+ "plugin_dependencies",
+ "version_available_stable",
+ "version_available_experimental",
+ "download_url_stable",
+ "download_url_experimental",
+ "create_date",
+ "update_date",
+ "create_date_stable",
+ "update_date_stable",
+ "create_date_experimental",
+ "update_date_experimental",
+ ]:
+ if (
+ attrib not in translatableAttributes or attrib == "name"
+ ): # include name!
if plugin.get(attrib, False):
self.mPlugins[key][attrib] = plugin[attrib]
# If the stable version is higher than the experimental version, we ignore the experimental version
- if compareVersions(self.mPlugins[key]["version_available_stable"], self.mPlugins[key]["version_available_experimental"]) == 1:
- self.mPlugins[key]["version_available_experimental"] = ''
+ if (
+ compareVersions(
+ self.mPlugins[key]["version_available_stable"],
+ self.mPlugins[key]["version_available_experimental"],
+ )
+ == 1
+ ):
+ self.mPlugins[key]["version_available_experimental"] = ""
# set status
#
@@ -769,60 +1095,129 @@ def rebuild(self):
# same same "installed"
# less greater "upgradeable"
# greater less "newer"
- if not self.mPlugins[key]["version_available_stable"] and not self.mPlugins[key]["version_installed"]:
+ if (
+ not self.mPlugins[key]["version_available_stable"]
+ and not self.mPlugins[key]["version_installed"]
+ ):
self.mPlugins[key]["status"] = "none available"
- elif not self.mPlugins[key]["version_available_stable"] and self.mPlugins[key]["version_installed"]:
+ elif (
+ not self.mPlugins[key]["version_available_stable"]
+ and self.mPlugins[key]["version_installed"]
+ ):
self.mPlugins[key]["status"] = "orphan"
elif not self.mPlugins[key]["version_installed"]:
self.mPlugins[key]["status"] = "not installed"
elif self.mPlugins[key]["version_installed"] in ["?", "-1"]:
self.mPlugins[key]["status"] = "installed"
- elif compareVersions(self.mPlugins[key]["version_available_stable"], self.mPlugins[key]["version_installed"]) == 0:
+ elif (
+ compareVersions(
+ self.mPlugins[key]["version_available_stable"],
+ self.mPlugins[key]["version_installed"],
+ )
+ == 0
+ ):
self.mPlugins[key]["status"] = "installed"
- elif compareVersions(self.mPlugins[key]["version_available_stable"], self.mPlugins[key]["version_installed"]) == 1:
+ elif (
+ compareVersions(
+ self.mPlugins[key]["version_available_stable"],
+ self.mPlugins[key]["version_installed"],
+ )
+ == 1
+ ):
self.mPlugins[key]["status"] = "upgradeable"
else:
self.mPlugins[key]["status"] = "newer"
# debug: test if the status match the "installed" tag:
- if self.mPlugins[key]["status"] in ["not installed", "none available"] and self.mPlugins[key]["installed"]:
- raise Exception("Error: plugin status is ambiguous (1) for plugin {}".format(key))
- if self.mPlugins[key]["status"] in ["installed", "orphan", "upgradeable", "newer"] and not self.mPlugins[key]["installed"]:
- raise Exception("Error: plugin status is ambiguous (2) for plugin {}".format(key))
-
- if not self.mPlugins[key]["version_available_experimental"] and not self.mPlugins[key]["version_installed"]:
+ if (
+ self.mPlugins[key]["status"]
+ in ["not installed", "none available"]
+ and self.mPlugins[key]["installed"]
+ ):
+ raise Exception(
+ f"Error: plugin status is ambiguous (1) for plugin {key}"
+ )
+ if (
+ self.mPlugins[key]["status"]
+ in ["installed", "orphan", "upgradeable", "newer"]
+ and not self.mPlugins[key]["installed"]
+ ):
+ raise Exception(
+ f"Error: plugin status is ambiguous (2) for plugin {key}"
+ )
+
+ if (
+ not self.mPlugins[key]["version_available_experimental"]
+ and not self.mPlugins[key]["version_installed"]
+ ):
self.mPlugins[key]["status_exp"] = "none available"
- elif not self.mPlugins[key]["version_available_experimental"] and self.mPlugins[key]["version_installed"]:
+ elif (
+ not self.mPlugins[key]["version_available_experimental"]
+ and self.mPlugins[key]["version_installed"]
+ ):
self.mPlugins[key]["status_exp"] = "orphan"
elif not self.mPlugins[key]["version_installed"]:
self.mPlugins[key]["status_exp"] = "not installed"
elif self.mPlugins[key]["version_installed"] in ["?", "-1"]:
self.mPlugins[key]["status_exp"] = "installed"
- elif compareVersions(self.mPlugins[key]["version_available_experimental"], self.mPlugins[key]["version_installed"]) == 0:
+ elif (
+ compareVersions(
+ self.mPlugins[key]["version_available_experimental"],
+ self.mPlugins[key]["version_installed"],
+ )
+ == 0
+ ):
self.mPlugins[key]["status_exp"] = "installed"
- elif compareVersions(self.mPlugins[key]["version_available_experimental"], self.mPlugins[key]["version_installed"]) == 1:
+ elif (
+ compareVersions(
+ self.mPlugins[key]["version_available_experimental"],
+ self.mPlugins[key]["version_installed"],
+ )
+ == 1
+ ):
self.mPlugins[key]["status_exp"] = "upgradeable"
else:
self.mPlugins[key]["status_exp"] = "newer"
# debug: test if the status_exp match the "installed" tag:
- if self.mPlugins[key]["status_exp"] in ["not installed", "none available"] and self.mPlugins[key]["installed"]:
- raise Exception("Error: plugin status_exp is ambiguous (1) for plugin {}".format(key))
- if self.mPlugins[key]["status_exp"] in ["installed", "orphan", "upgradeable", "newer"] and not self.mPlugins[key]["installed"]:
- raise Exception("Error: plugin status_exp is ambiguous (2) for plugin {} (status_exp={})".format(key, self.mPlugins[key]["status_exp"]))
+ if (
+ self.mPlugins[key]["status_exp"]
+ in ["not installed", "none available"]
+ and self.mPlugins[key]["installed"]
+ ):
+ raise Exception(
+ f"Error: plugin status_exp is ambiguous (1) for plugin {key}"
+ )
+ if (
+ self.mPlugins[key]["status_exp"]
+ in ["installed", "orphan", "upgradeable", "newer"]
+ and not self.mPlugins[key]["installed"]
+ ):
+ raise Exception(
+ "Error: plugin status_exp is ambiguous (2) for plugin {} (status_exp={})".format(
+ key, self.mPlugins[key]["status_exp"]
+ )
+ )
self.markNews()
# ----------------------------------------- #
def markNews(self):
- """ mark all new plugins as new """
- seenPlugins = QgsSettingsTree.node("plugin-manager").childSetting("seen-plugins").valueWithDefaultOverride(list(self.mPlugins.keys()))
+ """mark all new plugins as new"""
+ seenPlugins = (
+ QgsSettingsTree.node("plugin-manager")
+ .childSetting("seen-plugins")
+ .valueWithDefaultOverride(list(self.mPlugins.keys()))
+ )
if len(seenPlugins) > 0:
for plugin in list(self.mPlugins.keys()):
- if seenPlugins.count(plugin) == 0 and self.mPlugins[plugin]["status"] == "not installed":
+ if (
+ seenPlugins.count(plugin) == 0
+ and self.mPlugins[plugin]["status"] == "not installed"
+ ):
self.mPlugins[plugin]["status"] = "new"
# ----------------------------------------- #
def updateSeenPluginsList(self):
- """ update the list of all seen plugins """
+ """update the list of all seen plugins"""
setting = QgsSettingsTree.node("plugin-manager").childSetting("seen-plugins")
seenPlugins = setting.valueWithDefaultOverride(list(self.mPlugins.keys()))
for plugin in list(self.mPlugins.keys()):
@@ -832,7 +1227,7 @@ def updateSeenPluginsList(self):
# ----------------------------------------- #
def isThereAnythingNew(self):
- """ return true if an upgradeable or new plugin detected """
+ """return true if an upgradeable or new plugin detected"""
for i in list(self.mPlugins.values()):
if i["status"] in ["upgradeable", "new"]:
return True
diff --git a/python/pyplugin_installer/plugindependencies.py b/python/pyplugin_installer/plugindependencies.py
index c95e1f49b06d..4c1910208ca2 100644
--- a/python/pyplugin_installer/plugindependencies.py
+++ b/python/pyplugin_installer/plugindependencies.py
@@ -1,4 +1,3 @@
-# coding=utf-8
"""Parse plugin metadata for plugin_dependencies
.. note:: This program is free software; you can redistribute it and/or modify
@@ -8,9 +7,9 @@
"""
-__author__ = 'elpaso@itopen.it'
-__date__ = '2018-05-29'
-__copyright__ = 'Copyright 2018, GISCE-TI S.L.'
+__author__ = "elpaso@itopen.it"
+__date__ = "2018-05-29"
+__copyright__ = "Copyright 2018, GISCE-TI S.L."
from configparser import NoOptionError, NoSectionError
from .version_compare import compareVersions
@@ -19,13 +18,12 @@
def __plugin_name_map(plugin_data_values):
- return {
- plugin['name']: plugin['id']
- for plugin in plugin_data_values
- }
+ return {plugin["name"]: plugin["id"] for plugin in plugin_data_values}
-def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_plugins=None):
+def find_dependencies(
+ plugin_id, plugin_data=None, plugin_deps=None, installed_plugins=None
+):
"""Finds the plugin dependencies and checks if they can be installed or upgraded
:param plugin_id: plugin id
@@ -52,7 +50,12 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
if installed_plugins is None:
metadata_parser = metadataParser()
- installed_plugins = {metadata_parser[k].get('general', 'name'): metadata_parser[k].get('general', 'version') for k, v in metadata_parser.items()}
+ installed_plugins = {
+ metadata_parser[k]
+ .get("general", "name"): metadata_parser[k]
+ .get("general", "version")
+ for k, v in metadata_parser.items()
+ }
if plugin_data is None:
plugin_data = plugin_installer.plugins.all()
@@ -64,33 +67,49 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
try:
p_id = plugins_map[name]
except KeyError:
- not_found.update({name: {
- 'id': None,
- 'version_installed': None,
- 'version_required': None,
- 'version_available': None,
- 'use_stable_version': None,
- 'action': None,
- 'error': 'missing_id'
- }})
+ not_found.update(
+ {
+ name: {
+ "id": None,
+ "version_installed": None,
+ "version_required": None,
+ "version_available": None,
+ "use_stable_version": None,
+ "action": None,
+ "error": "missing_id",
+ }
+ }
+ )
continue
- affected_plugin = dict({
- "id": p_id,
- # "version_installed": installed_plugins.get(p_id, {}).get('installed_plugins', None),
- "version_installed": installed_plugins.get(name, None),
- "version_required": version_required,
- "version_available": plugin_data[p_id].get('version_available', None),
- "use_stable_version": True, # Prefer stable by default
- "action": None,
- })
- version_available_stable = plugin_data[p_id].get('version_available_stable', None)
- version_available_experimental = plugin_data[p_id].get('version_available_experimental', None)
-
- if version_required is not None and version_required == version_available_stable:
+ affected_plugin = dict(
+ {
+ "id": p_id,
+ # "version_installed": installed_plugins.get(p_id, {}).get('installed_plugins', None),
+ "version_installed": installed_plugins.get(name, None),
+ "version_required": version_required,
+ "version_available": plugin_data[p_id].get("version_available", None),
+ "use_stable_version": True, # Prefer stable by default
+ "action": None,
+ }
+ )
+ version_available_stable = plugin_data[p_id].get(
+ "version_available_stable", None
+ )
+ version_available_experimental = plugin_data[p_id].get(
+ "version_available_experimental", None
+ )
+
+ if (
+ version_required is not None
+ and version_required == version_available_stable
+ ):
affected_plugin["version_available"] = version_available_stable
affected_plugin["use_stable_version"] = True
- elif version_required is not None and version_required == version_available_experimental:
+ elif (
+ version_required is not None
+ and version_required == version_available_experimental
+ ):
affected_plugin["version_available"] = version_available_experimental
affected_plugin["use_stable_version"] = False
elif version_required is None:
@@ -104,21 +123,29 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
# Install is needed
if name not in installed_plugins:
- affected_plugin['action'] = 'install'
+ affected_plugin["action"] = "install"
destination_list = to_install
# Upgrade is needed
- elif version_required is not None and compareVersions(installed_plugins[name], version_required) == 2:
- affected_plugin['action'] = 'upgrade'
+ elif (
+ version_required is not None
+ and compareVersions(installed_plugins[name], version_required) == 2
+ ):
+ affected_plugin["action"] = "upgrade"
destination_list = to_upgrade
# TODO @elpaso: review installed but not activated
# No action is needed
else:
continue
- if version_required == affected_plugin['version_available'] or version_required is None:
+ if (
+ version_required == affected_plugin["version_available"]
+ or version_required is None
+ ):
destination_list.update({name: affected_plugin})
else:
- affected_plugin['error'] = 'unavailable {}'.format(affected_plugin['action'])
+ affected_plugin["error"] = "unavailable {}".format(
+ affected_plugin["action"]
+ )
not_found.update({name: affected_plugin})
return to_install, to_upgrade, not_found
diff --git a/python/pyplugin_installer/qgsplugindependenciesdialog.py b/python/pyplugin_installer/qgsplugindependenciesdialog.py
index ff13fbdeebde..cc8a0f943126 100644
--- a/python/pyplugin_installer/qgsplugindependenciesdialog.py
+++ b/python/pyplugin_installer/qgsplugindependenciesdialog.py
@@ -1,4 +1,3 @@
-# coding=utf-8
"""Plugin dependencies selection dialog
.. note:: This program is free software; you can redistribute it and/or modify
@@ -8,9 +7,9 @@
"""
-__author__ = 'elpaso@itopen.it'
-__date__ = '2018-09-19'
-__copyright__ = 'Copyright 2018, GISCE-TI S.L.'
+__author__ = "elpaso@itopen.it"
+__date__ = "2018-09-19"
+__copyright__ = "Copyright 2018, GISCE-TI S.L."
import os
@@ -20,10 +19,14 @@
from qgis.PyQt import QtWidgets, QtCore
from qgis.utils import iface
-Ui_QgsPluginDependenciesDialogBase, _ = uic.loadUiType(Path(__file__).parent / 'qgsplugindependenciesdialogbase.ui')
+Ui_QgsPluginDependenciesDialogBase, _ = uic.loadUiType(
+ Path(__file__).parent / "qgsplugindependenciesdialogbase.ui"
+)
-class QgsPluginDependenciesDialog(QtWidgets.QDialog, Ui_QgsPluginDependenciesDialogBase):
+class QgsPluginDependenciesDialog(
+ QtWidgets.QDialog, Ui_QgsPluginDependenciesDialogBase
+):
"""A dialog that shows plugin dependencies and offers a way to install or upgrade the
dependencies.
"""
@@ -46,11 +49,21 @@ def __init__(self, plugin_name, to_install, to_upgrade, not_found, parent=None):
super().__init__(parent)
self.setupUi(self)
self.setWindowTitle(self.tr("Plugin Dependencies Manager"))
- self.mPluginDependenciesLabel.setText(self.tr("Plugin dependencies for %s") % plugin_name)
+ self.mPluginDependenciesLabel.setText(
+ self.tr("Plugin dependencies for %s") % plugin_name
+ )
self.setStyleSheet("QTableView { padding: 20px;}")
# Name, Version Installed, Version Required, Version Available, Action Checkbox
self.pluginList.setColumnCount(5)
- self.pluginList.setHorizontalHeaderLabels([self.tr('Name'), self.tr('Installed'), self.tr('Required'), self.tr('Available'), self.tr('Action')])
+ self.pluginList.setHorizontalHeaderLabels(
+ [
+ self.tr("Name"),
+ self.tr("Installed"),
+ self.tr("Required"),
+ self.tr("Available"),
+ self.tr("Action"),
+ ]
+ )
self.pluginList.setRowCount(len(not_found) + len(to_install) + len(to_upgrade))
self.__actions = {}
@@ -61,18 +74,18 @@ def _display(txt):
def _make_row(data, i, name):
widget = QtWidgets.QLabel("%s" % name)
- widget.p_id = data['id']
- widget.action = data['action']
- widget.use_stable_version = data['use_stable_version']
+ widget.p_id = data["id"]
+ widget.action = data["action"]
+ widget.use_stable_version = data["use_stable_version"]
self.pluginList.setCellWidget(i, 0, widget)
self.pluginList.resizeColumnToContents(0)
- widget = QtWidgets.QTableWidgetItem(_display(data['version_installed']))
+ widget = QtWidgets.QTableWidgetItem(_display(data["version_installed"]))
widget.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
self.pluginList.setItem(i, 1, widget)
- widget = QtWidgets.QTableWidgetItem(_display(data['version_required']))
+ widget = QtWidgets.QTableWidgetItem(_display(data["version_required"]))
widget.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
self.pluginList.setItem(i, 2, widget)
- widget = QtWidgets.QTableWidgetItem(_display(data['version_available']))
+ widget = QtWidgets.QTableWidgetItem(_display(data["version_available"]))
widget.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
self.pluginList.setItem(i, 3, widget)
@@ -112,8 +125,11 @@ def accept(self):
try:
if self.pluginList.cellWidget(i, 4).isChecked():
self.__actions[self.pluginList.cellWidget(i, 0).p_id] = {
- 'action': self.pluginList.cellWidget(i, 0).action,
- 'use_stable_version': self.pluginList.cellWidget(i, 0).use_stable_version}
+ "action": self.pluginList.cellWidget(i, 0).action,
+ "use_stable_version": self.pluginList.cellWidget(
+ i, 0
+ ).use_stable_version,
+ }
except:
pass
super().accept()
diff --git a/python/pyplugin_installer/qgsplugininstallerfetchingdialog.py b/python/pyplugin_installer/qgsplugininstallerfetchingdialog.py
index c38e15b64cf8..aeee08dbc3c5 100644
--- a/python/pyplugin_installer/qgsplugininstallerfetchingdialog.py
+++ b/python/pyplugin_installer/qgsplugininstallerfetchingdialog.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
qgsplugininstallerfetchingdialog.py
@@ -33,10 +32,15 @@
from .installer_data import repositories
from qgis.gui import QgsGui
-Ui_QgsPluginInstallerFetchingDialogBase, _ = uic.loadUiType(Path(__file__).parent / 'qgsplugininstallerfetchingbase.ui')
+Ui_QgsPluginInstallerFetchingDialogBase, _ = uic.loadUiType(
+ Path(__file__).parent / "qgsplugininstallerfetchingbase.ui"
+)
-class QgsPluginInstallerFetchingDialog(QDialog, Ui_QgsPluginInstallerFetchingDialogBase):
+
+class QgsPluginInstallerFetchingDialog(
+ QDialog, Ui_QgsPluginInstallerFetchingDialogBase
+):
# ----------------------------------------- #
def __init__(self, parent):
@@ -65,13 +69,23 @@ def __init__(self, parent):
def displayState(self, key, state, state2=None):
messages = [
self.tr("Success"),
- QCoreApplication.translate('QgsPluginInstallerFetchingDialog', "Resolving host name…"),
- QCoreApplication.translate('QgsPluginInstallerFetchingDialog', "Connecting…"),
- QCoreApplication.translate('QgsPluginInstallerFetchingDialog', "Host connected. Sending request…"),
- QCoreApplication.translate('QgsPluginInstallerFetchingDialog', "Downloading data…"),
+ QCoreApplication.translate(
+ "QgsPluginInstallerFetchingDialog", "Resolving host name…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerFetchingDialog", "Connecting…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerFetchingDialog", "Host connected. Sending request…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerFetchingDialog", "Downloading data…"
+ ),
self.tr("Idle"),
- QCoreApplication.translate('QgsPluginInstallerFetchingDialog', "Closing connection…"),
- self.tr("Error")
+ QCoreApplication.translate(
+ "QgsPluginInstallerFetchingDialog", "Closing connection…"
+ ),
+ self.tr("Error"),
]
message = messages[state]
if state2:
diff --git a/python/pyplugin_installer/qgsplugininstallerinstallingdialog.py b/python/pyplugin_installer/qgsplugininstallerinstallingdialog.py
index d82c26d29dc3..ebd96422318f 100644
--- a/python/pyplugin_installer/qgsplugininstallerinstallingdialog.py
+++ b/python/pyplugin_installer/qgsplugininstallerinstallingdialog.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
qgsplugininstallerinstallingdialog.py
@@ -23,7 +22,6 @@
* *
***************************************************************************/
"""
-from builtins import str
from pathlib import Path
@@ -32,16 +30,24 @@
from qgis.PyQt.QtWidgets import QDialog
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
-from qgis.core import QgsNetworkAccessManager, QgsApplication, QgsNetworkRequestParameters
+from qgis.core import (
+ QgsNetworkAccessManager,
+ QgsApplication,
+ QgsNetworkRequestParameters,
+)
from qgis.utils import HOME_PLUGIN_PATH
from .installer_data import removeDir, repositories
from .unzip import unzip
-Ui_QgsPluginInstallerInstallingDialogBase, _ = uic.loadUiType(Path(__file__).parent / 'qgsplugininstallerinstallingbase.ui')
+Ui_QgsPluginInstallerInstallingDialogBase, _ = uic.loadUiType(
+ Path(__file__).parent / "qgsplugininstallerinstallingbase.ui"
+)
-class QgsPluginInstallerInstallingDialog(QDialog, Ui_QgsPluginInstallerInstallingDialogBase):
+class QgsPluginInstallerInstallingDialog(
+ QDialog, Ui_QgsPluginInstallerInstallingDialogBase
+):
# ----------------------------------------- #
def __init__(self, parent, plugin, stable=True):
@@ -54,7 +60,11 @@ def __init__(self, parent, plugin, stable=True):
self.labelName.setText(plugin["name"])
self.buttonBox.clicked.connect(self.abort)
- self.url = QUrl(plugin["download_url_stable"] if stable else plugin["download_url_experimental"])
+ self.url = QUrl(
+ plugin["download_url_stable"]
+ if stable
+ else plugin["download_url_experimental"]
+ )
self.redirectionCounter = 0
fileName = plugin["filename"]
@@ -66,14 +76,21 @@ def __init__(self, parent, plugin, stable=True):
def requestDownloading(self):
self.request = QNetworkRequest(self.url)
- self.request.setAttribute(QNetworkRequest.Attribute(QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass), "QgsPluginInstallerInstallingDialog")
+ self.request.setAttribute(
+ QNetworkRequest.Attribute(
+ QgsNetworkRequestParameters.RequestAttributes.AttributeInitiatorClass
+ ),
+ "QgsPluginInstallerInstallingDialog",
+ )
authcfg = repositories.all()[self.plugin["zip_repository"]]["authcfg"]
if authcfg and isinstance(authcfg, str):
if not QgsApplication.authManager().updateNetworkRequest(
- self.request, authcfg.strip()):
+ self.request, authcfg.strip()
+ ):
self.mResult = self.tr(
"Update of network request with authentication "
- "credentials FAILED for configuration '{0}'").format(authcfg)
+ "credentials FAILED for configuration '{0}'"
+ ).format(authcfg)
self.request = None
if self.request is not None:
@@ -96,14 +113,26 @@ def result(self):
# ----------------------------------------- #
def stateChanged(self, state):
messages = [
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Installing…"),
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Resolving host name…"),
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Connecting…"),
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Host connected. Sending request…"),
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Downloading data…"),
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Installing…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Resolving host name…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Connecting…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Host connected. Sending request…"
+ ),
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Downloading data…"
+ ),
self.tr("Idle"),
- QCoreApplication.translate('QgsPluginInstallerInstallingDialog', "Closing connection…"),
- self.tr("Error")
+ QCoreApplication.translate(
+ "QgsPluginInstallerInstallingDialog", "Closing connection…"
+ ),
+ self.tr("Error"),
]
self.labelState.setText(messages[state])
@@ -120,15 +149,25 @@ def requestFinished(self):
if reply.error() != QNetworkReply.NetworkError.NoError:
self.mResult = reply.errorString()
if reply.error() == QNetworkReply.NetworkError.OperationCanceledError:
- self.mResult += "
" + QCoreApplication.translate("QgsPluginInstaller", "If you haven't canceled the download manually, it might be caused by a timeout. In this case consider increasing the connection timeout value in QGIS options.")
+ self.mResult += "
" + QCoreApplication.translate(
+ "QgsPluginInstaller",
+ "If you haven't canceled the download manually, it might be caused by a timeout. In this case consider increasing the connection timeout value in QGIS options.",
+ )
self.reject()
reply.deleteLater()
return
- elif reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) in (301, 302):
- redirectionUrl = reply.attribute(QNetworkRequest.Attribute.RedirectionTargetAttribute)
+ elif reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) in (
+ 301,
+ 302,
+ ):
+ redirectionUrl = reply.attribute(
+ QNetworkRequest.Attribute.RedirectionTargetAttribute
+ )
self.redirectionCounter += 1
if self.redirectionCounter > 4:
- self.mResult = QCoreApplication.translate("QgsPluginInstaller", "Too many redirections")
+ self.mResult = QCoreApplication.translate(
+ "QgsPluginInstaller", "Too many redirections"
+ )
self.reject()
reply.deleteLater()
return
@@ -154,12 +193,22 @@ def requestFinished(self):
# if the target directory already exists as a link, remove the link without resolving:
QFile(pluginDir + str(QDir.separator()) + self.plugin["id"]).remove()
try:
- unzip(str(tmpPath), str(pluginDir)) # test extract. If fails, then exception will be raised and no removing occurs
+ unzip(
+ str(tmpPath), str(pluginDir)
+ ) # test extract. If fails, then exception will be raised and no removing occurs
# removing old plugin files if exist
- removeDir(QDir.cleanPath(pluginDir + "/" + self.plugin["id"])) # remove old plugin if exists
+ removeDir(
+ QDir.cleanPath(pluginDir + "/" + self.plugin["id"])
+ ) # remove old plugin if exists
unzip(str(tmpPath), str(pluginDir)) # final extract.
except:
- self.mResult = self.tr("Failed to unzip the plugin package. Probably it's broken or missing from the repository. You may also want to make sure that you have write permission to the plugin directory:") + "\n" + pluginDir
+ self.mResult = (
+ self.tr(
+ "Failed to unzip the plugin package. Probably it's broken or missing from the repository. You may also want to make sure that you have write permission to the plugin directory:"
+ )
+ + "\n"
+ + pluginDir
+ )
self.reject()
return
try:
diff --git a/python/pyplugin_installer/qgsplugininstallerpluginerrordialog.py b/python/pyplugin_installer/qgsplugininstallerpluginerrordialog.py
index 76f635969bab..ad92ef73d930 100644
--- a/python/pyplugin_installer/qgsplugininstallerpluginerrordialog.py
+++ b/python/pyplugin_installer/qgsplugininstallerpluginerrordialog.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
qgsplugininstallerpluginerrordialog.py
@@ -30,10 +29,14 @@
from qgis.PyQt import uic
-Ui_QgsPluginInstallerPluginErrorDialogBase, _ = uic.loadUiType(Path(__file__).parent / 'qgsplugininstallerpluginerrorbase.ui')
+Ui_QgsPluginInstallerPluginErrorDialogBase, _ = uic.loadUiType(
+ Path(__file__).parent / "qgsplugininstallerpluginerrorbase.ui"
+)
-class QgsPluginInstallerPluginErrorDialog(QDialog, Ui_QgsPluginInstallerPluginErrorDialogBase):
+class QgsPluginInstallerPluginErrorDialog(
+ QDialog, Ui_QgsPluginInstallerPluginErrorDialogBase
+):
# ----------------------------------------- #
def __init__(self, parent, errorMessage):
diff --git a/python/pyplugin_installer/qgsplugininstallerrepositorydialog.py b/python/pyplugin_installer/qgsplugininstallerrepositorydialog.py
index 80edeb84bab9..2499d859e4f6 100644
--- a/python/pyplugin_installer/qgsplugininstallerrepositorydialog.py
+++ b/python/pyplugin_installer/qgsplugininstallerrepositorydialog.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
qgsplugininstallerrepositorydialog.py
@@ -31,10 +30,14 @@
from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout
from qgis.PyQt.QtCore import Qt
-Ui_QgsPluginInstallerRepositoryDetailsDialogBase, _ = uic.loadUiType(Path(__file__).parent / 'qgsplugininstallerrepositorybase.ui')
+Ui_QgsPluginInstallerRepositoryDetailsDialogBase, _ = uic.loadUiType(
+ Path(__file__).parent / "qgsplugininstallerrepositorybase.ui"
+)
-class QgsPluginInstallerRepositoryDialog(QDialog, Ui_QgsPluginInstallerRepositoryDetailsDialogBase):
+class QgsPluginInstallerRepositoryDialog(
+ QDialog, Ui_QgsPluginInstallerRepositoryDetailsDialogBase
+):
# ----------------------------------------- #
def __init__(self, parent=None):
@@ -49,7 +52,7 @@ def __init__(self, parent=None):
# ----------------------------------------- #
def textChanged(self, string):
- enable = (len(self.editName.text()) > 0 and len(self.editURL.text()) > 0)
+ enable = len(self.editName.text()) > 0 and len(self.editURL.text()) > 0
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(enable)
def editAuthCfgId(self):
@@ -60,7 +63,9 @@ def editAuthCfgId(self):
if self.editAuthCfg.text():
selector.setConfigId(self.editAuthCfg.text())
layout.addWidget(selector)
- buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Close)
+ buttonBox = QDialogButtonBox(
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Close
+ )
buttonBox.accepted.connect(dlg.accept)
buttonBox.rejected.connect(dlg.reject)
layout.addWidget(buttonBox)
diff --git a/python/pyplugin_installer/unzip.py b/python/pyplugin_installer/unzip.py
index 7be620e75092..d4a80f8bcda4 100644
--- a/python/pyplugin_installer/unzip.py
+++ b/python/pyplugin_installer/unzip.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
"""
/***************************************************************************
Plugin Installer module
@@ -25,24 +24,24 @@
def unzip(file, targetDir, password=None):
- """ Creates directory structure and extracts the zip contents to it.
- file (file object) - the zip file to extract
- targetDir (str) - target location
- password (str; optional) - password to decrypt the zip file (if encrypted)
+ """Creates directory structure and extracts the zip contents to it.
+ file (file object) - the zip file to extract
+ targetDir (str) - target location
+ password (str; optional) - password to decrypt the zip file (if encrypted)
"""
# convert password to bytes
if isinstance(password, str):
- password = bytes(password, 'utf8')
+ password = bytes(password, "utf8")
# create destination directory if doesn't exist
- if not targetDir.endswith(':') and not os.path.exists(targetDir):
+ if not targetDir.endswith(":") and not os.path.exists(targetDir):
os.makedirs(targetDir)
zf = zipfile.ZipFile(file)
for name in zf.namelist():
# Skip directories - they will be created when necessary by os.makedirs
- if name.endswith('/'):
+ if name.endswith("/"):
continue
# Read the source file before creating any output,
@@ -56,7 +55,7 @@ def unzip(file, targetDir, password=None):
os.makedirs(fullDir)
# extract file
fullPath = os.path.normpath(os.path.join(targetDir, name))
- outfile = open(fullPath, 'wb')
+ outfile = open(fullPath, "wb")
outfile.write(memberContent)
outfile.flush()
outfile.close()
diff --git a/python/pyplugin_installer/version_compare.py b/python/pyplugin_installer/version_compare.py
index ebcb5c0924a9..2871062e2bc1 100644
--- a/python/pyplugin_installer/version_compare.py
+++ b/python/pyplugin_installer/version_compare.py
@@ -45,8 +45,7 @@
list is usually recognized as higher, except following suffixes:
ALPHA, BETA, RC, PREVIEW and TRUNK which make the version number lower.
"""
-from builtins import str
-from builtins import range
+
from qgis.core import Qgis
import re
@@ -56,21 +55,32 @@
def normalizeVersion(s):
- """ remove possible prefix from given string and convert to uppercase """
- prefixes = ['VERSION', 'VER.', 'VER', 'V.', 'V', 'REVISION', 'REV.', 'REV', 'R.', 'R']
+ """remove possible prefix from given string and convert to uppercase"""
+ prefixes = [
+ "VERSION",
+ "VER.",
+ "VER",
+ "V.",
+ "V",
+ "REVISION",
+ "REV.",
+ "REV",
+ "R.",
+ "R",
+ ]
if not s:
- return str()
+ return ""
s = str(s).upper()
for i in prefixes:
- if s[:len(i)] == i:
- s = s.replace(i, '')
+ if s[: len(i)] == i:
+ s = s.replace(i, "")
s = s.strip()
return s
# ------------------------------------------------------------------------ #
def classifyCharacter(c):
- """ return 0 for delimiter, 1 for digit and 2 for alphabetic character """
+ """return 0 for delimiter, 1 for digit and 2 for alphabetic character"""
if c in [".", "-", "_", " "]:
return 0
if c.isdigit():
@@ -81,7 +91,7 @@ def classifyCharacter(c):
# ------------------------------------------------------------------------ #
def chopString(s):
- """ convert string to list of numbers and words """
+ """convert string to list of numbers and words"""
l = [s[0]]
for i in range(1, len(s)):
if classifyCharacter(s[i]) == 0:
@@ -95,12 +105,19 @@ def chopString(s):
# ------------------------------------------------------------------------ #
def compareElements(s1, s2):
- """ compare two particular elements """
+ """compare two particular elements"""
# check if the matter is easy solvable:
if s1 == s2:
return 0
# try to compare as numeric values (but only if the first character is not 0):
- if s1 and s2 and s1.isnumeric() and s2.isnumeric() and s1[0] != '0' and s2[0] != '0':
+ if (
+ s1
+ and s2
+ and s1.isnumeric()
+ and s2.isnumeric()
+ and s1[0] != "0"
+ and s2[0] != "0"
+ ):
if float(s1) == float(s2):
return 0
elif float(s1) > float(s2):
@@ -109,10 +126,10 @@ def compareElements(s1, s2):
return 2
# if the strings aren't numeric or start from 0, compare them as a strings:
# but first, set ALPHA < BETA < PREVIEW < RC < TRUNK < [NOTHING] < [ANYTHING_ELSE]
- if s1 not in ['ALPHA', 'BETA', 'PREVIEW', 'RC', 'TRUNK']:
- s1 = 'Z' + s1
- if s2 not in ['ALPHA', 'BETA', 'PREVIEW', 'RC', 'TRUNK']:
- s2 = 'Z' + s2
+ if s1 not in ["ALPHA", "BETA", "PREVIEW", "RC", "TRUNK"]:
+ s1 = "Z" + s1
+ if s2 not in ["ALPHA", "BETA", "PREVIEW", "RC", "TRUNK"]:
+ s2 = "Z" + s2
# the final test:
if s1 > s2:
return 1
@@ -122,7 +139,7 @@ def compareElements(s1, s2):
# ------------------------------------------------------------------------ #
def compareVersions(a, b):
- """ Compare two version numbers. Return 0 if a==b or error, 1 if a>b and 2 if b>a """
+ """Compare two version numbers. Return 0 if a==b or error, 1 if a>b and 2 if b>a"""
if not a or not b:
return 0
a = normalizeVersion(a)
@@ -143,9 +160,9 @@ def compareVersions(a, b):
# if the lists are identical till the end of the shorter string, try to compare the odd tail
# with the simple space (because the 'alpha', 'beta', 'preview' and 'rc' are LESS then nothing)
if len(v1) > l:
- return compareElements(v1[l], ' ')
+ return compareElements(v1[l], " ")
if len(v2) > l:
- return compareElements(' ', v2[l])
+ return compareElements(" ", v2[l])
# if everything else fails...
if a > b:
return 1
@@ -160,10 +177,10 @@ def compareVersions(a, b):
def splitVersion(s):
- """ split string into 2 or 3 numerical segments """
+ """split string into 2 or 3 numerical segments"""
if not s or type(s) is not str:
return None
- l = str(s).split('.')
+ l = str(s).split(".")
for c in l:
if not c.isnumeric():
return None
@@ -175,14 +192,14 @@ def splitVersion(s):
def isCompatible(curVer, minVer, maxVer):
- """ Compare current QGIS version with qgisMinVersion and qgisMaxVersion """
+ """Compare current QGIS version with qgisMinVersion and qgisMaxVersion"""
if not minVer or not curVer or not maxVer:
return False
- minVer = splitVersion(re.sub(r'[^0-9.]+', '', minVer))
- maxVer = splitVersion(re.sub(r'[^0-9.]+', '', maxVer))
- curVer = splitVersion(re.sub(r'[^0-9.]+', '', curVer))
+ minVer = splitVersion(re.sub(r"[^0-9.]+", "", minVer))
+ maxVer = splitVersion(re.sub(r"[^0-9.]+", "", maxVer))
+ curVer = splitVersion(re.sub(r"[^0-9.]+", "", curVer))
if not minVer or not curVer or not maxVer:
return False
@@ -196,20 +213,20 @@ def isCompatible(curVer, minVer, maxVer):
if len(maxVer) < 3:
maxVer += ["99"]
- minVer = "{:04n}{:04n}{:04n}".format(int(minVer[0]), int(minVer[1]), int(minVer[2]))
- maxVer = "{:04n}{:04n}{:04n}".format(int(maxVer[0]), int(maxVer[1]), int(maxVer[2]))
- curVer = "{:04n}{:04n}{:04n}".format(int(curVer[0]), int(curVer[1]), int(curVer[2]))
+ minVer = f"{int(minVer[0]):04n}{int(minVer[1]):04n}{int(minVer[2]):04n}"
+ maxVer = f"{int(maxVer[0]):04n}{int(maxVer[1]):04n}{int(maxVer[2]):04n}"
+ curVer = f"{int(curVer[0]):04n}{int(curVer[1]):04n}{int(curVer[2]):04n}"
- return (minVer <= curVer and maxVer >= curVer)
+ return minVer <= curVer and maxVer >= curVer
def pyQgisVersion():
- """ Return current QGIS version number as X.Y.Z for testing plugin compatibility.
- If Y = 99, bump up to (X+1.0.0), so e.g. 2.99 becomes 3.0.0
- This way QGIS X.99 is only compatible with plugins for the upcoming major release.
+ """Return current QGIS version number as X.Y.Z for testing plugin compatibility.
+ If Y = 99, bump up to (X+1.0.0), so e.g. 2.99 becomes 3.0.0
+ This way QGIS X.99 is only compatible with plugins for the upcoming major release.
"""
- x, y, z = re.findall(r'^(\d*).(\d*).(\d*)', Qgis.QGIS_VERSION)[0]
- if y == '99':
+ x, y, z = re.findall(r"^(\d*).(\d*).(\d*)", Qgis.QGIS_VERSION)[0]
+ if y == "99":
x = str(int(x) + 1)
- y = z = '0'
- return '{}.{}.{}'.format(x, y, z)
+ y = z = "0"
+ return f"{x}.{y}.{z}"
diff --git a/python/testing/__init__.py b/python/testing/__init__.py
index f7608a27518f..33087f26de05 100644
--- a/python/testing/__init__.py
+++ b/python/testing/__init__.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
__init__.py
@@ -41,13 +39,9 @@
QDir,
QUrl,
QSize,
- QCoreApplication
-)
-from qgis.PyQt.QtGui import (
- QImage,
- QDesktopServices,
- QPainter
+ QCoreApplication,
)
+from qgis.PyQt.QtGui import QImage, QDesktopServices, QPainter
from qgis.core import (
QgsApplication,
QgsFeatureRequest,
@@ -71,12 +65,12 @@ def is_ci_run() -> bool:
"""
Returns True if the test is being run on the CI environment
"""
- return os.environ.get("QGIS_CONTINUOUS_INTEGRATION_RUN") == 'true'
+ return os.environ.get("QGIS_CONTINUOUS_INTEGRATION_RUN") == "true"
@classmethod
def setUpClass(cls):
- cls.report = ''
- cls.markdown_report = ''
+ cls.report = ""
+ cls.markdown_report = ""
@classmethod
def tearDownClass(cls):
@@ -98,32 +92,35 @@ def write_local_html_report(cls, report: str):
if not report_dir.exists():
QDir().mkpath(report_dir.path())
- report_file = report_dir.filePath('index.html')
+ report_file = report_dir.filePath("index.html")
# only append to existing reports if running under CI
file_is_empty = True
- if cls.is_ci_run() or \
- os.environ.get("QGIS_APPEND_TO_TEST_REPORT") == 'true':
- file_mode = 'ta'
+ if cls.is_ci_run() or os.environ.get("QGIS_APPEND_TO_TEST_REPORT") == "true":
+ file_mode = "ta"
try:
- with open(report_file, 'rt', encoding="utf-8") as f:
+ with open(report_file, encoding="utf-8") as f:
file_is_empty = not bool(f.read())
- except IOError:
+ except OSError:
pass
else:
- file_mode = 'wt'
+ file_mode = "wt"
- with open(report_file, file_mode, encoding='utf-8') as f:
+ with open(report_file, file_mode, encoding="utf-8") as f:
if file_is_empty:
from .test_data_dir import TEST_DATA_DIR
# append standard header
- with open(TEST_DATA_DIR + "/../test_report_header.html", 'rt', encoding='utf-8') as header_file:
+ with open(
+ TEST_DATA_DIR + "/../test_report_header.html", encoding="utf-8"
+ ) as header_file:
f.write(header_file.read())
# append embedded scripts
- f.write('\n")
@@ -153,7 +150,7 @@ def write_local_markdown_report(cls, report: str):
@classmethod
def get_test_caller_details(
cls,
- ) -> Tuple[Optional[str], Optional[str], Optional[int]]:
+ ) -> tuple[Optional[str], Optional[str], Optional[int]]:
"""
Retrieves the details of the caller at the earliest position
in the stack, excluding unittest internals.
@@ -186,7 +183,7 @@ def image_check(
size_tolerance: Optional[Union[int, QSize]] = None,
expect_fail: bool = False,
control_path_prefix: Optional[str] = None,
- use_checkerboard_background: bool = False
+ use_checkerboard_background: bool = False,
) -> bool:
if use_checkerboard_background:
output_image = QImage(image.size(), QImage.Format.Format_RGB32)
@@ -217,7 +214,9 @@ def image_check(
if size_tolerance is not None:
if isinstance(size_tolerance, QSize):
if size_tolerance.isValid():
- checker.setSizeTolerance(size_tolerance.width(), size_tolerance.height())
+ checker.setSizeTolerance(
+ size_tolerance.width(), size_tolerance.height()
+ )
else:
checker.setSizeTolerance(size_tolerance, size_tolerance)
@@ -228,7 +227,7 @@ def image_check(
markdown = checker.markdownReport()
if markdown:
- cls.markdown_report += "## {}\n\n".format(name)
+ cls.markdown_report += f"## {name}\n\n"
cls.markdown_report += markdown
return result
@@ -242,7 +241,7 @@ def render_map_settings_check(
control_name=None,
color_tolerance: Optional[int] = None,
allowed_mismatch: Optional[int] = None,
- control_path_prefix: Optional[str] = None
+ control_path_prefix: Optional[str] = None,
) -> bool:
checker = QgsMultiRenderChecker()
checker.setMapSettings(map_settings)
@@ -265,19 +264,20 @@ def render_map_settings_check(
markdown = checker.markdownReport()
if markdown:
- cls.markdown_report += "## {}\n\n".format(name)
+ cls.markdown_report += f"## {name}\n\n"
cls.markdown_report += markdown
return result
@classmethod
def render_layout_check(
- cls, name: str,
+ cls,
+ name: str,
layout: QgsLayout,
size: Optional[QSize] = None,
color_tolerance: Optional[int] = None,
allowed_mismatch: Optional[int] = None,
- page: Optional[int] = 0
+ page: Optional[int] = 0,
) -> bool:
checker = QgsLayoutChecker(name, layout)
@@ -292,15 +292,14 @@ def render_layout_check(
if cls.control_path_prefix():
checker.setControlPathPrefix(cls.control_path_prefix())
- result, message = checker.testLayout(page=page,
- pixelDiff=allowed_mismatch or 0)
+ result, message = checker.testLayout(page=page, pixelDiff=allowed_mismatch or 0)
if not result:
cls.report += f"
Render {name}
\n"
cls.report += checker.report()
markdown = checker.markdownReport()
if markdown:
- cls.markdown_report += "## {}\n\n".format(name)
+ cls.markdown_report += f"## {name}\n\n"
cls.markdown_report += markdown
return result
@@ -313,9 +312,8 @@ def get_test_data_path(file_path: str) -> Path:
"""
from utilities import unitTestDataPath
- return (
- Path(unitTestDataPath()) /
- (file_path[1:] if file_path.startswith('/') else file_path)
+ return Path(unitTestDataPath()) / (
+ file_path[1:] if file_path.startswith("/") else file_path
)
def assertLayersEqual(self, layer_expected, layer_result, **kwargs):
@@ -335,7 +333,9 @@ def assertLayersEqual(self, layer_expected, layer_result, **kwargs):
"""
self.checkLayersEqual(layer_expected, layer_result, True, **kwargs)
- def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kwargs):
+ def checkLayersEqual(
+ self, layer_expected, layer_result, use_asserts=False, **kwargs
+ ):
"""
:param layer_expected: The first layer to compare
:param layer_result: The second layer to compare
@@ -354,23 +354,36 @@ def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kw
"""
try:
- request = kwargs['request']
+ request = kwargs["request"]
except KeyError:
request = QgsFeatureRequest()
try:
- compare = kwargs['compare']
+ compare = kwargs["compare"]
except KeyError:
compare = {}
# Compare CRS
- if 'ignore_crs_check' not in compare or not compare['ignore_crs_check']:
- expected_wkt = layer_expected.dataProvider().crs().toWkt(QgsCoordinateReferenceSystem.WktVariant.WKT_PREFERRED)
- result_wkt = layer_result.dataProvider().crs().toWkt(QgsCoordinateReferenceSystem.WktVariant.WKT_PREFERRED)
+ if "ignore_crs_check" not in compare or not compare["ignore_crs_check"]:
+ expected_wkt = (
+ layer_expected.dataProvider()
+ .crs()
+ .toWkt(QgsCoordinateReferenceSystem.WktVariant.WKT_PREFERRED)
+ )
+ result_wkt = (
+ layer_result.dataProvider()
+ .crs()
+ .toWkt(QgsCoordinateReferenceSystem.WktVariant.WKT_PREFERRED)
+ )
if use_asserts:
- self.assertEqual(layer_expected.dataProvider().crs(), layer_result.dataProvider().crs())
- elif layer_expected.dataProvider().crs() != layer_result.dataProvider().crs():
+ self.assertEqual(
+ layer_expected.dataProvider().crs(),
+ layer_result.dataProvider().crs(),
+ )
+ elif (
+ layer_expected.dataProvider().crs() != layer_result.dataProvider().crs()
+ ):
return False
# Compare features
@@ -380,42 +393,42 @@ def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kw
return False
try:
- precision = compare['geometry']['precision']
+ precision = compare["geometry"]["precision"]
except KeyError:
precision = 14
try:
- topo_equal_check = compare['geometry']['topo_equal_check']
+ topo_equal_check = compare["geometry"]["topo_equal_check"]
except KeyError:
topo_equal_check = False
try:
- ignore_part_order = compare['geometry']['ignore_part_order']
+ ignore_part_order = compare["geometry"]["ignore_part_order"]
except KeyError:
ignore_part_order = False
try:
- normalize = compare['geometry']['normalize']
+ normalize = compare["geometry"]["normalize"]
except KeyError:
normalize = False
try:
- explode_collections = compare['geometry']['explode_collections']
+ explode_collections = compare["geometry"]["explode_collections"]
except KeyError:
explode_collections = False
try:
- snap_to_grid = compare['geometry']['snap_to_grid']
+ snap_to_grid = compare["geometry"]["snap_to_grid"]
except KeyError:
snap_to_grid = None
try:
- unordered = compare['unordered']
+ unordered = compare["unordered"]
except KeyError:
unordered = False
try:
- equate_null_and_empty = compare['geometry']['equate_null_and_empty']
+ equate_null_and_empty = compare["geometry"]["equate_null_and_empty"]
except KeyError:
equate_null_and_empty = False
@@ -424,13 +437,22 @@ def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kw
for feat in layer_result.getFeatures(request):
feat_expected_equal = None
for feat_expected in features_expected:
- if self.checkGeometriesEqual(feat.geometry(), feat_expected.geometry(),
- feat.id(), feat_expected.id(),
- False, precision, topo_equal_check, ignore_part_order, normalize=normalize,
- explode_collections=explode_collections,
- snap_to_grid=snap_to_grid,
- equate_null_and_empty=equate_null_and_empty) and \
- self.checkAttributesEqual(feat, feat_expected, layer_expected.fields(), False, compare):
+ if self.checkGeometriesEqual(
+ feat.geometry(),
+ feat_expected.geometry(),
+ feat.id(),
+ feat_expected.id(),
+ False,
+ precision,
+ topo_equal_check,
+ ignore_part_order,
+ normalize=normalize,
+ explode_collections=explode_collections,
+ snap_to_grid=snap_to_grid,
+ equate_null_and_empty=equate_null_and_empty,
+ ) and self.checkAttributesEqual(
+ feat, feat_expected, layer_expected.fields(), False, compare
+ ):
feat_expected_equal = feat_expected
break
@@ -440,10 +462,15 @@ def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kw
if use_asserts:
self.assertTrue(
False,
- 'Unexpected result feature: fid {}, geometry: {}, attributes: {}'.format(
+ "Unexpected result feature: fid {}, geometry: {}, attributes: {}".format(
feat.id(),
- feat.geometry().constGet().asWkt(precision) if feat.geometry() else 'NULL',
- feat.attributes())
+ (
+ feat.geometry().constGet().asWkt(precision)
+ if feat.geometry()
+ else "NULL"
+ ),
+ feat.attributes(),
+ ),
)
else:
return False
@@ -452,24 +479,34 @@ def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kw
if use_asserts:
lst_missing = []
for feat in features_expected:
- lst_missing.append('fid {}, geometry: {}, attributes: {}'.format(
- feat.id(),
- feat.geometry().constGet().asWkt(precision) if feat.geometry() else 'NULL',
- feat.attributes())
+ lst_missing.append(
+ "fid {}, geometry: {}, attributes: {}".format(
+ feat.id(),
+ (
+ feat.geometry().constGet().asWkt(precision)
+ if feat.geometry()
+ else "NULL"
+ ),
+ feat.attributes(),
+ )
)
- self.assertTrue(False, 'Some expected features not found in results:\n' + '\n'.join(lst_missing))
+ self.assertTrue(
+ False,
+ "Some expected features not found in results:\n"
+ + "\n".join(lst_missing),
+ )
else:
return False
return True
def get_pk_or_fid(f):
- if 'pk' in kwargs and kwargs['pk'] is not None:
- key = kwargs['pk']
+ if "pk" in kwargs and kwargs["pk"] is not None:
+ key = kwargs["pk"]
if isinstance(key, list) or isinstance(key, tuple):
return [f[k] for k in key]
else:
- return f[kwargs['pk']]
+ return f[kwargs["pk"]]
else:
return f.id()
@@ -481,39 +518,53 @@ def sort_by_pk_or_fid(f):
pk = [(v == NULL, v) for v in pk]
return (pk == NULL, pk)
- expected_features = sorted(layer_expected.getFeatures(request), key=sort_by_pk_or_fid)
- result_features = sorted(layer_result.getFeatures(request), key=sort_by_pk_or_fid)
+ expected_features = sorted(
+ layer_expected.getFeatures(request), key=sort_by_pk_or_fid
+ )
+ result_features = sorted(
+ layer_result.getFeatures(request), key=sort_by_pk_or_fid
+ )
for feats in zip(expected_features, result_features):
- eq = self.checkGeometriesEqual(feats[0].geometry(),
- feats[1].geometry(),
- feats[0].id(),
- feats[1].id(),
- use_asserts, precision, topo_equal_check, ignore_part_order, normalize=normalize, explode_collections=explode_collections,
- snap_to_grid=snap_to_grid, equate_null_and_empty=equate_null_and_empty)
+ eq = self.checkGeometriesEqual(
+ feats[0].geometry(),
+ feats[1].geometry(),
+ feats[0].id(),
+ feats[1].id(),
+ use_asserts,
+ precision,
+ topo_equal_check,
+ ignore_part_order,
+ normalize=normalize,
+ explode_collections=explode_collections,
+ snap_to_grid=snap_to_grid,
+ equate_null_and_empty=equate_null_and_empty,
+ )
if not eq and not use_asserts:
return False
- eq = self.checkAttributesEqual(feats[0], feats[1], layer_expected.fields(), use_asserts, compare)
+ eq = self.checkAttributesEqual(
+ feats[0], feats[1], layer_expected.fields(), use_asserts, compare
+ )
if not eq and not use_asserts:
return False
return True
def checkFilesEqual(self, filepath_expected, filepath_result, use_asserts=False):
- with open(filepath_expected, 'r') as file_expected:
- with open(filepath_result, 'r') as file_result:
+ with open(filepath_expected) as file_expected:
+ with open(filepath_result) as file_result:
diff = difflib.unified_diff(
file_expected.readlines(),
file_result.readlines(),
- fromfile='expected',
- tofile='result',
+ fromfile="expected",
+ tofile="result",
)
diff = list(diff)
eq = not len(diff)
if use_asserts:
- self.assertEqual(0, len(diff), ''.join(diff))
+ self.assertEqual(0, len(diff), "".join(diff))
else:
return eq
@@ -529,8 +580,16 @@ def assertDirectoryEqual(self, dirpath_expected: str, dirpath_result: str):
contents_result = list(path_result.iterdir())
contents_expected = list(path_expected.iterdir())
- contents_expected = [p for p in contents_expected if p.suffix != '.png' or not p.stem.endswith('_mask')]
- self.assertCountEqual([p.name if p.is_file() else p.stem for p in contents_expected], [p.name if p.is_file() else p.stem for p in contents_result], f'Directory contents mismatch in {dirpath_expected} vs {dirpath_result}')
+ contents_expected = [
+ p
+ for p in contents_expected
+ if p.suffix != ".png" or not p.stem.endswith("_mask")
+ ]
+ self.assertCountEqual(
+ [p.name if p.is_file() else p.stem for p in contents_expected],
+ [p.name if p.is_file() else p.stem for p in contents_result],
+ f"Directory contents mismatch in {dirpath_expected} vs {dirpath_result}",
+ )
# compare file contents
for expected_file_path in contents_expected:
@@ -539,23 +598,29 @@ def assertDirectoryEqual(self, dirpath_expected: str, dirpath_result: str):
result_file_path = path_result / expected_file_path.name
- if expected_file_path.suffix == '.pbf':
+ if expected_file_path.suffix == ".pbf":
# vector layer, use assertLayersEqual
- layer_expected = QgsVectorLayer(str(expected_file_path), 'Expected')
+ layer_expected = QgsVectorLayer(str(expected_file_path), "Expected")
self.assertTrue(layer_expected.isValid())
- layer_result = QgsVectorLayer(str(result_file_path), 'Result')
+ layer_result = QgsVectorLayer(str(result_file_path), "Result")
self.assertTrue(layer_result.isValid())
self.assertLayersEqual(layer_expected, layer_result)
- elif expected_file_path.suffix == '.png':
+ elif expected_file_path.suffix == ".png":
# image file, use QgsRenderChecker
checker = QgsRenderChecker()
- res = checker.compareImages(expected_file_path.stem, expected_file_path.as_posix(), result_file_path.as_posix())
+ res = checker.compareImages(
+ expected_file_path.stem,
+ expected_file_path.as_posix(),
+ result_file_path.as_posix(),
+ )
self.assertTrue(res)
else:
- assert False, f"Don't know how to compare {expected_file_path.suffix} files"
+ assert (
+ False
+ ), f"Don't know how to compare {expected_file_path.suffix} files"
def assertDirectoriesEqual(self, dirpath_expected: str, dirpath_result: str):
- """ Checks whether both directories have the same content (recursively) and raises an assertion error if not. """
+ """Checks whether both directories have the same content (recursively) and raises an assertion error if not."""
self.assertDirectoryEqual(dirpath_expected, dirpath_result)
# recurse through subfolders
@@ -565,26 +630,70 @@ def assertDirectoriesEqual(self, dirpath_expected: str, dirpath_result: str):
if p.is_dir():
self.assertDirectoriesEqual(str(p), path_result / p.stem)
- def assertGeometriesEqual(self, geom0, geom1, geom0_id='geometry 1', geom1_id='geometry 2', precision=14, topo_equal_check=False, ignore_part_order=False, normalize=False, explode_collections=False, snap_to_grid=None, equate_null_and_empty=False):
- self.checkGeometriesEqual(geom0, geom1, geom0_id, geom1_id, use_asserts=True, precision=precision, topo_equal_check=topo_equal_check, ignore_part_order=ignore_part_order, normalize=normalize, explode_collections=explode_collections, snap_to_grid=snap_to_grid, equate_null_and_empty=equate_null_and_empty)
+ def assertGeometriesEqual(
+ self,
+ geom0,
+ geom1,
+ geom0_id="geometry 1",
+ geom1_id="geometry 2",
+ precision=14,
+ topo_equal_check=False,
+ ignore_part_order=False,
+ normalize=False,
+ explode_collections=False,
+ snap_to_grid=None,
+ equate_null_and_empty=False,
+ ):
+ self.checkGeometriesEqual(
+ geom0,
+ geom1,
+ geom0_id,
+ geom1_id,
+ use_asserts=True,
+ precision=precision,
+ topo_equal_check=topo_equal_check,
+ ignore_part_order=ignore_part_order,
+ normalize=normalize,
+ explode_collections=explode_collections,
+ snap_to_grid=snap_to_grid,
+ equate_null_and_empty=equate_null_and_empty,
+ )
- def checkGeometriesEqual(self, geom0, geom1, geom0_id, geom1_id, use_asserts=False, precision=14, topo_equal_check=False, ignore_part_order=False, normalize=False, explode_collections=False, snap_to_grid=None, equate_null_and_empty=False):
- """ Checks whether two geometries are the same - using either a strict check of coordinates (up to given precision)
+ def checkGeometriesEqual(
+ self,
+ geom0,
+ geom1,
+ geom0_id,
+ geom1_id,
+ use_asserts=False,
+ precision=14,
+ topo_equal_check=False,
+ ignore_part_order=False,
+ normalize=False,
+ explode_collections=False,
+ snap_to_grid=None,
+ equate_null_and_empty=False,
+ ):
+ """Checks whether two geometries are the same - using either a strict check of coordinates (up to given precision)
or by using topological equality (where e.g. a polygon with clockwise is equal to a polygon with counter-clockwise
order of vertices)
.. versionadded:: 3.2
"""
- geom0_wkt = ''
- geom0_wkt_full = ''
- geom1_wkt = ''
- geom1_wkt_full = ''
+ geom0_wkt = ""
+ geom0_wkt_full = ""
+ geom1_wkt = ""
+ geom1_wkt_full = ""
geom0_is_null = geom0.isNull() or (equate_null_and_empty and geom0.isEmpty())
geom1_is_null = geom1.isNull() or (equate_null_and_empty and geom1.isEmpty())
if not geom0_is_null and not geom1_is_null:
if snap_to_grid is not None:
- geom0 = geom0.snappedToGrid(snap_to_grid, snap_to_grid, snap_to_grid, snap_to_grid)
- geom1 = geom1.snappedToGrid(snap_to_grid, snap_to_grid, snap_to_grid, snap_to_grid)
+ geom0 = geom0.snappedToGrid(
+ snap_to_grid, snap_to_grid, snap_to_grid, snap_to_grid
+ )
+ geom1 = geom1.snappedToGrid(
+ snap_to_grid, snap_to_grid, snap_to_grid, snap_to_grid
+ )
if normalize:
geom0.normalize()
geom1.normalize()
@@ -602,7 +711,9 @@ def checkGeometriesEqual(self, geom0, geom1, geom0_id, geom1_id, use_asserts=Fal
if not equal and topo_equal_check:
equal = geom0.isGeosEqual(geom1)
if not equal and ignore_part_order and geom0.isMultipart():
- equal = sorted([p.asWkt(precision) for p in geom0.constParts()]) == sorted([p.asWkt(precision) for p in geom1.constParts()])
+ equal = sorted(
+ [p.asWkt(precision) for p in geom0.constParts()]
+ ) == sorted([p.asWkt(precision) for p in geom1.constParts()])
elif geom0_is_null and geom1_is_null:
equal = True
else:
@@ -614,90 +725,102 @@ def checkGeometriesEqual(self, geom0, geom1, geom0_id, geom1_id, use_asserts=Fal
if use_asserts:
self.assertTrue(
- equal, ''
- ' Features (Expected fid: {}, Result fid: {}) differ in geometry with method {}: \n\n'
- ' At given precision ({}):\n'
- ' Expected geometry: {}\n'
- ' Result geometry: {}\n\n'
- ' Full precision:\n'
- ' Expected geometry : {}\n'
- ' Result geometry: {}\n\n'.format(
+ equal,
+ ""
+ " Features (Expected fid: {}, Result fid: {}) differ in geometry with method {}: \n\n"
+ " At given precision ({}):\n"
+ " Expected geometry: {}\n"
+ " Result geometry: {}\n\n"
+ " Full precision:\n"
+ " Expected geometry : {}\n"
+ " Result geometry: {}\n\n".format(
geom0_id,
geom1_id,
- 'geos' if topo_equal_check else 'wkt',
+ "geos" if topo_equal_check else "wkt",
precision,
- geom0_wkt if not geom0_is_null else 'NULL',
- geom1_wkt if not geom1_is_null else 'NULL',
- geom0_wkt_full if not geom0_is_null else 'NULL',
- geom1_wkt_full if not geom1_is_null else 'NULL'
- )
+ geom0_wkt if not geom0_is_null else "NULL",
+ geom1_wkt if not geom1_is_null else "NULL",
+ geom0_wkt_full if not geom0_is_null else "NULL",
+ geom1_wkt_full if not geom1_is_null else "NULL",
+ ),
)
else:
return equal
def checkAttributesEqual(self, feat0, feat1, fields_expected, use_asserts, compare):
- """ Checks whether attributes of two features are the same
+ """Checks whether attributes of two features are the same
.. versionadded:: 3.2
"""
- for attr_expected, field_expected in zip(feat0.attributes(), fields_expected.toList()):
+ for attr_expected, field_expected in zip(
+ feat0.attributes(), fields_expected.toList()
+ ):
try:
- cmp = compare['fields'][field_expected.name()]
+ cmp = compare["fields"][field_expected.name()]
except KeyError:
try:
- cmp = compare['fields']['__all__']
+ cmp = compare["fields"]["__all__"]
except KeyError:
cmp = {}
# Skip field
- if 'skip' in cmp:
+ if "skip" in cmp:
continue
if use_asserts:
self.assertIn(
field_expected.name().lower(),
- [name.lower() for name in feat1.fields().names()])
+ [name.lower() for name in feat1.fields().names()],
+ )
attr_result = feat1[field_expected.name()]
- field_result = [fld for fld in fields_expected.toList() if fld.name() == field_expected.name()][0]
+ field_result = [
+ fld
+ for fld in fields_expected.toList()
+ if fld.name() == field_expected.name()
+ ][0]
# Cast field to a given type
isNumber = False
- if 'cast' in cmp:
- if cmp['cast'] == 'int':
+ if "cast" in cmp:
+ if cmp["cast"] == "int":
attr_expected = int(attr_expected) if attr_expected else None
attr_result = int(attr_result) if attr_result else None
isNumber = True
- if cmp['cast'] == 'float':
+ if cmp["cast"] == "float":
attr_expected = float(attr_expected) if attr_expected else None
attr_result = float(attr_result) if attr_result else None
isNumber = True
- if cmp['cast'] == 'str':
+ if cmp["cast"] == "str":
if isinstance(attr_expected, QDateTime):
- attr_expected = attr_expected.toString('yyyy/MM/dd hh:mm:ss')
+ attr_expected = attr_expected.toString("yyyy/MM/dd hh:mm:ss")
elif isinstance(attr_expected, QDate):
- attr_expected = attr_expected.toString('yyyy/MM/dd')
+ attr_expected = attr_expected.toString("yyyy/MM/dd")
else:
attr_expected = str(attr_expected) if attr_expected else None
if isinstance(attr_result, QDateTime):
- attr_result = attr_result.toString('yyyy/MM/dd hh:mm:ss')
+ attr_result = attr_result.toString("yyyy/MM/dd hh:mm:ss")
elif isinstance(attr_result, QDate):
- attr_result = attr_result.toString('yyyy/MM/dd')
+ attr_result = attr_result.toString("yyyy/MM/dd")
else:
attr_result = str(attr_result) if attr_result else None
# Round field (only numeric so it works with __all__)
- if 'precision' in cmp and (field_expected.type() in [QVariant.Int, QVariant.Double, QVariant.LongLong] or isNumber):
+ if "precision" in cmp and (
+ field_expected.type()
+ in [QVariant.Int, QVariant.Double, QVariant.LongLong]
+ or isNumber
+ ):
if not attr_expected == NULL:
- attr_expected = round(attr_expected, cmp['precision'])
+ attr_expected = round(attr_expected, cmp["precision"])
if not attr_result == NULL:
- attr_result = round(attr_result, cmp['precision'])
+ attr_result = round(attr_result, cmp["precision"])
if use_asserts:
self.assertEqual(
attr_expected,
attr_result,
- 'Features {}/{} differ in attributes\n\n * Field expected: {} ({})\n * result : {} ({})\n\n * Expected: {} != Result : {}'.format(
+ "Features {}/{} differ in attributes\n\n * Field expected: {} ({})\n * result : {} ({})\n\n * Expected: {} != Result : {}".format(
feat0.id(),
feat1.id(),
field_expected.name(),
@@ -705,8 +828,8 @@ def checkAttributesEqual(self, feat0, feat1, fields_expected, use_asserts, compa
field_result.name(),
field_result.typeName(),
repr(attr_expected),
- repr(attr_result)
- )
+ repr(attr_result),
+ ),
)
elif attr_expected != attr_result:
return False
@@ -715,10 +838,10 @@ def checkAttributesEqual(self, feat0, feat1, fields_expected, use_asserts, compa
class _UnexpectedSuccess(Exception):
-
"""
The test was supposed to fail, but it didn't!
"""
+
pass
@@ -740,7 +863,7 @@ def my_test(self):
def my_test(self):
self.assertTrue(qgisIsInvented())
"""
- if hasattr(args[0], '__call__'):
+ if hasattr(args[0], "__call__"):
# We got a function as parameter: assume usage like
# @expectedFailure
# def testfunction():
@@ -754,6 +877,7 @@ def wrapper(*args, **kwargs):
pass
else:
raise _UnexpectedSuccess
+
return wrapper
else:
# We got a function as parameter: assume usage like
@@ -773,6 +897,7 @@ def wrapper(*args, **kwargs):
raise _UnexpectedSuccess
else:
func(*args, **kwargs)
+
return wrapper
return realExpectedFailure
@@ -782,64 +907,100 @@ def wrapper(*args, **kwargs):
def _deprecatedAssertLayersEqual(*args, **kwargs):
- warn('unittest.TestCase.assertLayersEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.assertLayersEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.assertLayersEqual(*args, **kwargs)
def _deprecatedCheckLayersEqual(*args, **kwargs):
- warn('unittest.TestCase.checkLayersEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.checkLayersEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.checkLayersEqual(*args, **kwargs)
def _deprecatedAssertFilesEqual(*args, **kwargs):
- warn('unittest.TestCase.assertFilesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.assertFilesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.assertFilesEqual(*args, **kwargs)
def _deprecatedCheckFilesEqual(*args, **kwargs):
- warn('unittest.TestCase.checkFilesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.checkFilesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.checkFilesEqual(*args, **kwargs)
def _deprecatedAssertDirectoryEqual(*args, **kwargs):
- warn('unittest.TestCase.assertDirectoryEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.assertDirectoryEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.assertDirectoryEqual(*args, **kwargs)
def _deprecatedAssertDirectoriesEqual(*args, **kwargs):
- warn('unittest.TestCase.assertDirectoriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.assertDirectoriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.assertDirectoriesEqual(*args, **kwargs)
def _deprecatedAssertGeometriesEqual(*args, **kwargs):
- warn('unittest.TestCase.assertGeometriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.assertGeometriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.assertGeometriesEqual(*args, **kwargs)
def _deprecatedCheckGeometriesEqual(*args, **kwargs):
- warn('unittest.TestCase.checkGeometriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.checkGeometriesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.checkGeometriesEqual(*args, **kwargs)
def _deprecatedCheckAttributesEqual(*args, **kwargs):
- warn('unittest.TestCase.checkAttributesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.checkAttributesEqual is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
return QgisTestCase.checkAttributesEqual(*args, **kwargs)
def _deprecated_image_check(*args, **kwargs):
- warn('unittest.TestCase.image_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.image_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
# Remove the first args element `self` which we don't need for a @classmethod
return QgisTestCase.image_check(*args[1:], **kwargs)
def _deprecated_render_map_settings_check(*args, **kwargs):
- warn('unittest.TestCase.render_map_settings_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.render_map_settings_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
# Remove the first args element `self` which we don't need for a @classmethod
return QgisTestCase.render_map_settings_check(*args[1:], **kwargs)
def _deprecated_render_layout_check(*args, **kwargs):
- warn('unittest.TestCase.render_layout_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`', DeprecationWarning)
+ warn(
+ "unittest.TestCase.render_layout_check is deprecated and will be removed in the future. Port your tests to `qgis.testing.TestCase`",
+ DeprecationWarning,
+ )
# Remove the first args element `self` which we don't need for a @classmethod
return QgisTestCase.render_layout_check(*args[1:], **kwargs)
@@ -890,7 +1051,7 @@ def start_app(cleanup=True):
try:
sys.argv
except AttributeError:
- sys.argv = ['']
+ sys.argv = [""]
# In python3 we need to convert to a bytes object (or should
# QgsApplication accept a QString instead of const char* ?)
@@ -899,22 +1060,26 @@ def start_app(cleanup=True):
except AttributeError:
argvb = sys.argv
- QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts, True)
+ QCoreApplication.setAttribute(
+ Qt.ApplicationAttribute.AA_ShareOpenGLContexts, True
+ )
# Note: QGIS_PREFIX_PATH is evaluated in QgsApplication -
# no need to mess with it here.
QGISAPP = QgsApplication(argvb, myGuiFlag)
- tmpdir = tempfile.mkdtemp('', 'QGIS-PythonTestConfigPath-')
- os.environ['QGIS_CUSTOM_CONFIG_PATH'] = tmpdir
+ tmpdir = tempfile.mkdtemp("", "QGIS-PythonTestConfigPath-")
+ os.environ["QGIS_CUSTOM_CONFIG_PATH"] = tmpdir
QGISAPP.initQgis()
print(QGISAPP.showSettings())
def debug_log_message(message, tag, level):
- print('{}({}): {}'.format(tag, level, message))
+ print(f"{tag}({level}): {message}")
- QgsApplication.instance().messageLog().messageReceived.connect(debug_log_message)
+ QgsApplication.instance().messageLog().messageReceived.connect(
+ debug_log_message
+ )
if cleanup:
import atexit
diff --git a/python/testing/mocked.py b/python/testing/mocked.py
index b871d739f829..e94a0d62ab20 100644
--- a/python/testing/mocked.py
+++ b/python/testing/mocked.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
mocked
@@ -17,13 +15,13 @@
***************************************************************************
"""
-__author__ = 'Matthias Kuhn'
-__date__ = 'January 2016'
-__copyright__ = '(C) 2016, Matthias Kuhn'
+__author__ = "Matthias Kuhn"
+__date__ = "January 2016"
+__copyright__ = "(C) 2016, Matthias Kuhn"
import os
import sys
-import mock
+from unittest import mock
from qgis.gui import QgisInterface, QgsMapCanvas
from qgis.core import QgsApplication
diff --git a/python/user.py b/python/user.py
index 49b0718bc832..fafbe530f272 100644
--- a/python/user.py
+++ b/python/user.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
user.py
@@ -17,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Nathan Woodrow'
-__date__ = 'January 2015'
-__copyright__ = '(C) 2015, Nathan Woodrow'
+__author__ = "Nathan Woodrow"
+__date__ = "January 2015"
+__copyright__ = "(C) 2015, Nathan Woodrow"
import os
import sys
@@ -43,12 +41,16 @@ def load_user_expressions(path):
# As user expression functions should be registered with qgsfunction
# just importing the file is enough to get it to load the functions into QGIS
try:
- __import__("expressions.{0}".format(name), locals(), globals())
+ __import__(f"expressions.{name}", locals(), globals())
except:
error = traceback.format_exc()
msgtitle = QCoreApplication.translate("UserExpressions", "User expressions")
- msg = QCoreApplication.translate("UserExpressions", "The user expression {0} is not valid").format(name)
- QgsMessageLog.logMessage(msg + "\n" + error, msgtitle, Qgis.MessageLevel.Warning)
+ msg = QCoreApplication.translate(
+ "UserExpressions", "The user expression {0} is not valid"
+ ).format(name)
+ QgsMessageLog.logMessage(
+ msg + "\n" + error, msgtitle, Qgis.MessageLevel.Warning
+ )
def reload_user_expressions(path):
@@ -63,12 +65,12 @@ def reload_user_expressions(path):
if name == "__init__":
continue
- mod = "expressions.{0}".format(name)
+ mod = f"expressions.{name}"
if mod not in sys.modules:
continue
# try removing path
- if hasattr(sys.modules[mod], '__path__'):
+ if hasattr(sys.modules[mod], "__path__"):
for path in sys.modules[mod].__path__:
try:
sys.path.remove(path)
diff --git a/python/utils.py b/python/utils.py
index 0a27f68f0b02..45378ba7456c 100644
--- a/python/utils.py
+++ b/python/utils.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""
***************************************************************************
utils.py
@@ -17,9 +15,9 @@
***************************************************************************
"""
-__author__ = 'Martin Dobias'
-__date__ = 'November 2009'
-__copyright__ = '(C) 2009, Martin Dobias'
+__author__ = "Martin Dobias"
+__date__ = "November 2009"
+__copyright__ = "(C) 2009, Martin Dobias"
"""
QGIS utilities module
@@ -27,7 +25,14 @@
"""
from typing import List, Dict, Optional
-from qgis.PyQt.QtCore import QT_VERSION_STR, QCoreApplication, QLocale, QThread, qDebug, QUrl
+from qgis.PyQt.QtCore import (
+ QT_VERSION_STR,
+ QCoreApplication,
+ QLocale,
+ QThread,
+ qDebug,
+ QUrl,
+)
from qgis.PyQt.QtGui import QDesktopServices
from qgis.PyQt.QtWidgets import QPushButton, QApplication
from qgis.core import Qgis, QgsMessageLog, qgsfunction, QgsMessageOutput
@@ -45,44 +50,49 @@
import functools
import builtins
-builtins.__dict__['unicode'] = str
-builtins.__dict__['basestring'] = str
-builtins.__dict__['long'] = int
-builtins.__dict__['Set'] = set
+
+builtins.__dict__["unicode"] = str
+builtins.__dict__["basestring"] = str
+builtins.__dict__["long"] = int
+builtins.__dict__["Set"] = set
# ######################
# ERROR HANDLING
-warnings.simplefilter('default')
+warnings.simplefilter("default")
warnings.filterwarnings("ignore", "the sets module is deprecated")
def showWarning(message, category, filename, lineno, file=None, line=None):
stk = ""
for s in traceback.format_stack()[:-2]:
- if hasattr(s, 'decode'):
+ if hasattr(s, "decode"):
stk += s.decode(sys.getfilesystemencoding())
else:
stk += s
- if hasattr(filename, 'decode'):
+ if hasattr(filename, "decode"):
decoded_filename = filename.decode(sys.getfilesystemencoding())
else:
decoded_filename = filename
QgsMessageLog.logMessage(
- "warning:{}\ntraceback:{}".format(warnings.formatwarning(message, category, decoded_filename, lineno), stk),
- QCoreApplication.translate("Python", "Python warning")
+ f"warning:{warnings.formatwarning(message, category, decoded_filename, lineno)}\ntraceback:{stk}",
+ QCoreApplication.translate("Python", "Python warning"),
)
-def showException(type, value, tb, msg, messagebar=False, level=Qgis.MessageLevel.Warning):
+def showException(
+ type, value, tb, msg, messagebar=False, level=Qgis.MessageLevel.Warning
+):
if msg is None:
- msg = QCoreApplication.translate('Python', 'An error has occurred while executing Python code:')
+ msg = QCoreApplication.translate(
+ "Python", "An error has occurred while executing Python code:"
+ )
- logmessage = ''
+ logmessage = ""
for s in traceback.format_exception(type, value, tb):
- logmessage += s.decode('utf-8', 'replace') if hasattr(s, 'decode') else s
+ logmessage += s.decode("utf-8", "replace") if hasattr(s, "decode") else s
- title = QCoreApplication.translate('Python', 'Python error')
+ title = QCoreApplication.translate("Python", "Python error")
QgsMessageLog.logMessage(logmessage, title, level)
try:
@@ -111,10 +121,23 @@ def showException(type, value, tb, msg, messagebar=False, level=Qgis.MessageLeve
# Return of we already have a message with the same error message
return
- widget = bar.createMessage(title, msg + " " + QCoreApplication.translate("Python", "See message log (Python Error) for more details."))
+ widget = bar.createMessage(
+ title,
+ msg
+ + " "
+ + QCoreApplication.translate(
+ "Python", "See message log (Python Error) for more details."
+ ),
+ )
widget.setProperty("Error", msg)
- stackbutton = QPushButton(QCoreApplication.translate("Python", "Stack trace"), pressed=functools.partial(open_stack_dialog, type, value, tb, msg))
- button = QPushButton(QCoreApplication.translate("Python", "View message log"), pressed=show_message_log)
+ stackbutton = QPushButton(
+ QCoreApplication.translate("Python", "Stack trace"),
+ pressed=functools.partial(open_stack_dialog, type, value, tb, msg),
+ )
+ button = QPushButton(
+ QCoreApplication.translate("Python", "View message log"),
+ pressed=show_message_log,
+ )
widget.layout().addWidget(stackbutton)
widget.layout().addWidget(button)
bar.pushWidget(widget, Qgis.MessageLevel.Warning)
@@ -132,10 +155,12 @@ def open_stack_dialog(type, value, tb, msg, pop_error=True):
iface.messageBar().popWidget()
if msg is None:
- msg = QCoreApplication.translate('Python', 'An error has occurred while executing Python code:')
+ msg = QCoreApplication.translate(
+ "Python", "An error has occurred while executing Python code:"
+ )
# TODO Move this to a template HTML file
- txt = '''{msg}
+ txt = """{msg}