# -*- coding: utf-8 -*-
# /***************************************************************************
# Irmt
# A QGIS plugin
# OpenQuake Integrated Risk Modelling Toolkit
# -------------------
# begin : 2013-10-24
# copyright : (C) 2014 by GEM Foundation
# email : devops@openquake.org
# ***************************************************************************/
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.
import json
from qgis.PyQt.QtCore import pyqtSlot, QSettings, Qt
from qgis.PyQt.QtWidgets import QDialog, QColorDialog, QMessageBox
from qgis.PyQt.QtGui import QPalette
from qgis.core import QgsGraduatedSymbolRenderer, QgsProject
from qgis.gui import QgsMessageBar
from requests import Session
from svir.dialogs.connection_profile_dialog import ConnectionProfileDialog
from svir.utilities.utils import (
get_ui_class,
get_style,
platform_login,
engine_login,
log_msg,
WaitCursorManager,
check_is_lockdown,
)
from svir.utilities.shared import (
PLATFORM_REGISTRATION_URL,
DEFAULT_SETTINGS,
DEFAULT_PLATFORM_PROFILES,
DEFAULT_ENGINE_PROFILES,
LOG_LEVELS,
)
FORM_CLASS = get_ui_class('ui_settings.ui')
[docs]class SettingsDialog(QDialog, FORM_CLASS):
"""
Dialog used to edit the plugin settings
"""
def __init__(self, iface, irmt_main=None, parent=None):
QDialog.__init__(self, parent)
self.iface = iface
self.parent = parent
# in order to reset login when new credentials are saved
self.irmt_main = irmt_main
# Set up the user interface from Designer.
self.setupUi(self)
self.message_bar = QgsMessageBar(self)
self.layout().insertWidget(0, self.message_bar)
link_text = ('<a href="%s">Register to the OpenQuake Platform</a>'
% PLATFORM_REGISTRATION_URL)
self.registration_link_lbl.setText(link_text)
self.style_color_from.setFocusPolicy(Qt.NoFocus)
self.style_color_to.setFocusPolicy(Qt.NoFocus)
modes = {
QgsGraduatedSymbolRenderer.EqualInterval: self.tr(
'Equal Interval'),
QgsGraduatedSymbolRenderer.Quantile: self.tr(
'Quantile (Equal Count)'),
QgsGraduatedSymbolRenderer.Jenks: self.tr(
'Natural Breaks (Jenks)'),
QgsGraduatedSymbolRenderer.StdDev: self.tr('Standard Deviation'),
QgsGraduatedSymbolRenderer.Pretty: self.tr('Pretty Breaks'),
}
for key in modes:
self.style_mode.addItem(modes[key], key)
for log_level in sorted(LOG_LEVELS):
self.log_level_cbx.addItem(LOG_LEVELS[log_level], log_level)
self.restore_state()
self.initial_engine_hostname = QSettings().value(
'irmt/engine_hostname')
[docs] def restore_state(self, restore_defaults=False):
"""
Reinstate the options based on the user's stored session info.
:param restore_defaults: if True, settings will be reset to the default
values, instead of reading them from the user's stored info
"""
mySettings = QSettings()
self.refresh_profile_cbxs('platform', restore_defaults)
self.refresh_profile_cbxs('engine', restore_defaults)
developer_mode = (DEFAULT_SETTINGS['developer_mode']
if restore_defaults
else mySettings.value(
'irmt/developer_mode', False, type=bool))
experimental_enabled = (DEFAULT_SETTINGS['experimental_enabled']
if restore_defaults
else mySettings.value(
'irmt/experimental_enabled',
False, type=bool))
log_level = (DEFAULT_SETTINGS['log_level']
if restore_defaults
else mySettings.value(
'irmt/log_level',
DEFAULT_SETTINGS['log_level']))
self.log_level_cbx.setCurrentIndex(
self.log_level_cbx.findData(log_level))
style = get_style(
self.iface.activeLayer(),
self.iface.messageBar(),
restore_defaults)
self.set_button_color(self.style_color_from, style['color_from'])
self.set_button_color(self.style_color_to, style['color_to'])
mode_idx = self.style_mode.findData(style['mode'])
self.style_mode.setCurrentIndex(mode_idx)
self.style_classes.setValue(style['classes'])
self.force_restyling_ckb.setChecked(style['force_restyling'])
self.developer_mode_ckb.setChecked(developer_mode)
self.enable_experimental_ckb.setChecked(experimental_enabled)
[docs] def refresh_profile_cbxs(self, platform_or_engine, restore_defaults=False):
assert platform_or_engine in ('platform', 'engine'), platform_or_engine
if platform_or_engine == 'platform':
self.platform_profile_cbx.blockSignals(True)
self.platform_profile_cbx.clear()
self.platform_profile_cbx.blockSignals(False)
else: # 'engine'
self.engine_profile_cbx.blockSignals(True)
self.engine_profile_cbx.clear()
self.engine_profile_cbx.blockSignals(False)
mySettings = QSettings()
if restore_defaults:
profiles = json.loads(
DEFAULT_PLATFORM_PROFILES if platform_or_engine == 'platform'
else DEFAULT_ENGINE_PROFILES)
cur_profile = list(profiles.keys())[0]
else:
profiles = json.loads(
mySettings.value(
'irmt/%s_profiles' % platform_or_engine,
(DEFAULT_PLATFORM_PROFILES
if platform_or_engine == 'platform'
else DEFAULT_ENGINE_PROFILES)))
cur_profile = mySettings.value(
'irmt/current_%s_profile' % platform_or_engine)
for profile in sorted(profiles, key=str.lower):
if platform_or_engine == 'platform':
self.platform_profile_cbx.blockSignals(True)
self.platform_profile_cbx.addItem(profile)
self.platform_profile_cbx.blockSignals(False)
else: # engine
self.engine_profile_cbx.blockSignals(True)
self.engine_profile_cbx.addItem(profile)
self.engine_profile_cbx.blockSignals(False)
if cur_profile is None:
cur_profile = list(profiles.keys())[0]
mySettings.setValue(
'irmt/current_%s_profile' % platform_or_engine,
cur_profile)
if platform_or_engine == 'platform':
self.platform_profile_cbx.setCurrentIndex(
self.platform_profile_cbx.findText(cur_profile))
else: # engine
self.engine_profile_cbx.setCurrentIndex(
self.engine_profile_cbx.findText(cur_profile))
if platform_or_engine == 'platform':
self.pla_remove_btn.setEnabled(
self.platform_profile_cbx.count() > 1)
else: # engine
self.eng_remove_btn.setEnabled(
self.engine_profile_cbx.count() > 1)
[docs] def save_state(self):
"""
Store the options into the user's stored session info.
"""
mySettings = QSettings()
mySettings.setValue('irmt/developer_mode',
self.developer_mode_ckb.isChecked())
mySettings.setValue('irmt/experimental_enabled',
self.enable_experimental_ckb.isChecked())
mySettings.setValue(
'irmt/log_level',
self.log_level_cbx.itemData(self.log_level_cbx.currentIndex()))
cur_pla_profile = self.platform_profile_cbx.currentText()
cur_eng_profile = self.engine_profile_cbx.currentText()
platform_profiles = json.loads(mySettings.value(
'irmt/platform_profiles', DEFAULT_PLATFORM_PROFILES))
platform_profile = platform_profiles[cur_pla_profile]
engine_profiles = json.loads(mySettings.value(
'irmt/engine_profiles', DEFAULT_ENGINE_PROFILES))
engine_profile = engine_profiles[cur_eng_profile]
mySettings.setValue('irmt/platform_hostname',
platform_profile['hostname'])
mySettings.setValue('irmt/platform_username',
platform_profile['username'])
mySettings.setValue('irmt/platform_password',
platform_profile['password'])
mySettings.setValue('irmt/engine_hostname',
engine_profile['hostname'])
mySettings.setValue('irmt/engine_username',
engine_profile['username'])
mySettings.setValue('irmt/engine_password',
engine_profile['password'])
color_from = self.style_color_from.palette().color(QPalette.Button)
mySettings.setValue(
'irmt/style_color_from',
color_from.rgba())
color_to = self.style_color_to.palette().color(QPalette.Button)
mySettings.setValue(
'irmt/style_color_to',
color_to.rgba())
mySettings.setValue('irmt/style_mode', self.style_mode.itemData(
self.style_mode.currentIndex()))
mySettings.setValue('irmt/style_classes', self.style_classes.value())
active_layer = self.iface.activeLayer()
# at project level, save the setting associated to the layer if
# available
if active_layer is not None:
# NOTE: We can't use %s/%s instead of %s_%s, because / is a special
# character
QgsProject.instance().writeEntry(
'irmt', '%s_%s' % (active_layer.id(), 'force_restyling'),
self.force_restyling_ckb.isChecked())
else: # no layer is selected
QgsProject.instance().writeEntry(
'irmt', 'force_restyling',
self.force_restyling_ckb.isChecked())
# keep the latest setting saved also into the general settings
mySettings.setValue('irmt/force_restyling',
self.force_restyling_ckb.isChecked())
@pyqtSlot()
[docs] def on_style_color_from_clicked(self):
self.select_color(self.style_color_from)
@pyqtSlot()
[docs] def on_style_color_to_clicked(self):
self.select_color(self.style_color_to)
@pyqtSlot()
[docs] def on_restore_default_settings_btn_clicked(self):
msg = ("All current settings will be deleted and replaced by defaults."
" Are you sure?")
reply = QMessageBox.question(
self, 'Warning', msg, QMessageBox.Yes, QMessageBox.No)
if reply == QMessageBox.Yes:
self.restore_state(restore_defaults=True)
@pyqtSlot(int)
@pyqtSlot(int)
[docs] def on_engine_profile_cbx_currentIndexChanged(self, idx):
profile = self.engine_profile_cbx.itemText(idx)
QSettings().setValue('irmt/current_engine_profile', profile)
@pyqtSlot()
[docs] def on_pla_edit_btn_clicked(self):
profile_name = self.platform_profile_cbx.currentText()
self.profile_dlg = ConnectionProfileDialog(
'platform', profile_name, parent=self)
if self.profile_dlg.exec_():
self.refresh_profile_cbxs('platform')
@pyqtSlot()
[docs] def on_eng_edit_btn_clicked(self):
profile_name = self.engine_profile_cbx.currentText()
self.profile_dlg = ConnectionProfileDialog(
'engine', profile_name, parent=self)
if self.profile_dlg.exec_():
self.refresh_profile_cbxs('engine')
@pyqtSlot()
[docs] def on_pla_new_btn_clicked(self):
self.profile_dlg = ConnectionProfileDialog(
'platform', parent=self)
if self.profile_dlg.exec_():
self.refresh_profile_cbxs('platform')
@pyqtSlot()
[docs] def on_pla_test_btn_clicked(self):
server = 'platform'
profile_name = self.platform_profile_cbx.currentText()
with WaitCursorManager('Logging in...', self.message_bar):
self._attempt_login(server, profile_name)
@pyqtSlot()
[docs] def on_eng_test_btn_clicked(self):
server = 'engine'
profile_name = self.engine_profile_cbx.currentText()
with WaitCursorManager('Logging in...', self.message_bar):
self._attempt_login(server, profile_name)
def _attempt_login(self, server, profile_name):
if server == 'platform':
default_profiles = DEFAULT_PLATFORM_PROFILES
login_func = platform_login
elif server == 'engine':
default_profiles = DEFAULT_ENGINE_PROFILES
login_func = engine_login
else:
raise NotImplementedError(server)
mySettings = QSettings()
profiles = json.loads(mySettings.value(
'irmt/%s_profiles' % server, default_profiles))
profile = profiles[profile_name]
session = Session()
hostname, username, password = (profile['hostname'],
profile['username'],
profile['password'])
if server == 'engine':
try:
is_lockdown = check_is_lockdown(hostname, session)
except Exception as exc:
err_msg = ("Unable to connect"
" (see Log Message Panel for details)")
log_msg(err_msg, level='C', message_bar=self.message_bar,
exception=exc)
return
else:
if not is_lockdown:
msg = 'Able to connect'
log_msg(msg, level='S', message_bar=self.message_bar,
duration=3)
return
try:
login_func(hostname, username, password, session)
except Exception as exc:
err_msg = "Unable to connect (see Log Message Panel for details)"
log_msg(err_msg, level='C', message_bar=self.message_bar,
exception=exc)
else:
msg = 'Able to connect'
log_msg(msg, level='S', message_bar=self.message_bar, duration=3)
@pyqtSlot()
[docs] def on_eng_new_btn_clicked(self):
self.profile_dlg = ConnectionProfileDialog(
'engine', parent=self)
if self.profile_dlg.exec_():
self.refresh_profile_cbxs('engine')
@pyqtSlot()
[docs] def on_pla_remove_btn_clicked(self):
self.remove_selected_profile('platform')
@pyqtSlot()
[docs] def on_eng_remove_btn_clicked(self):
self.remove_selected_profile('engine')
[docs] def remove_selected_profile(self, platform_or_engine):
assert platform_or_engine in ('platform', 'engine'), platform_or_engine
if QMessageBox.question(
self,
'Remove connection profile',
('If you continue, the selected profile will be permanently'
' deleted. Are you sure?'),
QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
return
profiles = json.loads(
QSettings().value('irmt/%s_profiles' % platform_or_engine))
if platform_or_engine == 'platform':
cur_profile = self.platform_profile_cbx.currentText()
else: # engine
cur_profile = self.engine_profile_cbx.currentText()
del profiles[cur_profile]
QSettings().remove('irmt/current_%s_profile' % platform_or_engine)
self.save_profiles(platform_or_engine, profiles)
self.refresh_profile_cbxs(platform_or_engine)
[docs] def save_profiles(self, platform_or_engine, profiles):
assert platform_or_engine in ('platform', 'engine'), platform_or_engine
QSettings().setValue('irmt/%s_profiles' % platform_or_engine,
json.dumps(profiles))
[docs] def select_color(self, button):
initial = button.palette().color(QPalette.Button)
color = QColorDialog.getColor(initial)
if color.isValid():
self.set_button_color(button, color)
[docs] def accept(self):
"""
Method invoked when OK button is clicked.
"""
self.save_state()
if self.irmt_main is not None:
self.irmt_main.reset_engine_login()
current_engine_hostname = QSettings().value('irmt/engine_hostname')
# in case the engine hostname was modified, the embedded web apps that
# were using the old hostname must be refreshed using the new one
# (set_host is called in their __init__ when dialogs are created, and
# it needs to be called again here if those dialogs are already
# initialized and pointing to a previous engine server)
if (current_engine_hostname != self.initial_engine_hostname
and self.irmt_main is not None):
for dlg in (self.irmt_main.ipt_dlg,
self.irmt_main.taxtweb_dlg,
self.irmt_main.taxonomy_dlg):
if dlg is not None:
dlg.set_host()
dlg.load_homepage()
super(SettingsDialog, self).accept()