Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX - Add a new dock for HTML maptip preview #525

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions lizmap/dialogs/dock_html_preview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
__copyright__ = 'Copyright 2023, 3Liz'
__license__ = 'GPL version 3'
__email__ = '[email protected]'

import logging

from qgis.core import (
Qgis,
QgsApplication,
QgsExpression,
QgsExpressionContext,
QgsExpressionContextUtils,
QgsMapLayerProxyModel,
QgsProject,
)

if Qgis.QGIS_VERSION_INT >= 31400:
from qgis.gui import QgsFeaturePickerWidget

from qgis.gui import QgsMapLayerComboBox
from qgis.PyQt.QtCore import QDateTime, QLocale, QUrl
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import (
QDockWidget,
QHBoxLayout,
QLabel,
QPushButton,
QSizePolicy,
QVBoxLayout,
QWidget,
)
from qgis.utils import iface

from lizmap.qgis_plugin_tools.tools.i18n import tr
from lizmap.qgis_plugin_tools.tools.resources import resources_path

try:
from qgis.PyQt.QtWebKitWidgets import QWebView
WEBKIT_AVAILABLE = True
except ModuleNotFoundError:
WEBKIT_AVAILABLE = False

LOGGER = logging.getLogger('Lizmap')


class HtmlPreview(QDockWidget):

# noinspection PyArgumentList
def __init__(self, parent, *__args):
""" Constructor. """
super().__init__(parent, *__args)
self.setWindowTitle("Lizmap HTML Maptip Preview")

self._server_url = None

self.dock = QWidget(parent)
self.layout = QVBoxLayout(self.dock)

if Qgis.QGIS_VERSION_INT < 31400:
self.label = QLabel('You must install at least QGIS 3.14')
self.label.setWordWrap(True)
self.layout.addWidget(self.label)
return

if not WEBKIT_AVAILABLE:
self.label = QLabel(tr('You must install Qt Webkit to enable this feature.'))
else:
self.label = QLabel(tr("This only a preview of the HTML maptip. Lizmap will add more CSS classes."))

self.label.setWordWrap(True)
self.layout.addWidget(self.label)

if not WEBKIT_AVAILABLE:
return

horizontal = QHBoxLayout(self.dock)

self.layer = QgsMapLayerComboBox(self.dock)
horizontal.addWidget(self.layer)

self.feature = QgsFeaturePickerWidget(self.dock)
horizontal.addWidget(self.feature)

self.layout.addLayout(horizontal)

horizontal = QHBoxLayout(self.dock)

self.refresh = QPushButton(self.dock)
self.refresh.setIcon(QIcon(QgsApplication.iconPath('mActionRefresh.svg')))
# noinspection PyUnresolvedReferences
self.refresh.clicked.connect(self.update_html)
horizontal.addWidget(self.refresh)

self.label = QLabel()
horizontal.addWidget(self.label)

self.layout.addLayout(horizontal)

self.web_view = QWebView(self.dock)
size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.web_view.sizePolicy().hasHeightForWidth())
self.web_view.setSizePolicy(size_policy)
self.layout.addWidget(self.web_view)

self.setWidget(self.dock)

self.layer.setFilters(QgsMapLayerProxyModel.VectorLayer)
# noinspection PyUnresolvedReferences
self.layer.layerChanged.connect(self.current_layer_changed)
self.current_layer_changed()
# noinspection PyUnresolvedReferences
self.feature.featureChanged.connect(self.update_html)
self.feature.setShowBrowserButtons(True)

# We don't have a better signal to listen to
QgsProject.instance().dirtySet.connect(self.update_html)

self.update_html()

def set_server_url(self, url: str):
""" Set the server URL according to the main dialog. """
if not url.endswith('/'):
url += '/'
self._server_url = url

def css(self) -> str:
""" Link to CSS style sheet according to server. """
asset = 'assets/css/bootstrap.min.css'
return self._server_url + asset

def current_layer_changed(self):
""" When the layer has changed. """
self.feature.setLayer(self.layer.currentLayer())
# Need to disconnect all layers before ?
# self.layer.currentLayer().repaintRequested.connect(self.update_html())

# noinspection PyArgumentList
def update_html(self):
""" Update the HTML preview. """
# This function is called when the project is "setDirty",
# because it means maybe the vector layer properties has been "applied"

if not self.isVisible():
# If the dock is not visible, we don't care
return

layer = self.layer.currentLayer()
if not layer:
return

if iface.activeLayer() != layer:
# This function is called when the project is "setDirty",
# because it means maybe the vector layer properties has been "applied"
return

feature = self.feature.feature()
if not feature:
return

now = QDateTime.currentDateTime()
now_str = now.toString(QLocale.c().timeFormat(QLocale.ShortFormat))
self.label.setText(tr("Last update") + " " + now_str)

exp_context = QgsExpressionContext()
exp_context.appendScope(QgsExpressionContextUtils.globalScope())
exp_context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))
exp_context.setFeature(feature)
html_string = QgsExpression.replaceExpressionText(layer.mapTipTemplate(), exp_context)
base_url = QUrl.fromLocalFile(resources_path('images', 'non_existing_file.png'))

with open(resources_path('html', 'maptip_preview.html'), encoding='utf8') as f:
html_template = f.read()

html_content = html_template.format(
css=self.css(),
maptip=html_string,
)

self.web_view.setHtml(html_content, base_url)
27 changes: 27 additions & 0 deletions lizmap/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
from lizmap.definitions.time_manager import TimeManagerDefinitions
from lizmap.definitions.tooltip import ToolTipDefinitions
from lizmap.definitions.warnings import Warnings
from lizmap.dialogs.dock_html_preview import HtmlPreview
from lizmap.dialogs.html_editor import HtmlEditorDialog
from lizmap.dialogs.html_maptip import HtmlMapTipDialog
from lizmap.dialogs.lizmap_popup import LizmapPopupDialog
Expand Down Expand Up @@ -223,6 +224,7 @@ def __init__(self, iface):
self.version = version()
self.is_dev_version = any(item in self.version for item in UNSTABLE_VERSION_PREFIX)
self.dlg = LizmapDialog(is_dev_version=self.is_dev_version)
self.dock_html_preview = None
self.version_checker = None
if self.is_dev_version:

Expand Down Expand Up @@ -833,6 +835,11 @@ def target_server_changed(self):
self.dlg.check_qgis_version(widget=True)
self.check_webdav()

if self.dock_html_preview:
# Change the URL for the CSS
self.dock_html_preview: HtmlPreview
self.dock_html_preview.set_server_url(self.dlg.current_server_info(ServerComboData.ServerUrl.value))

lizmap_cloud = is_lizmap_cloud(current_metadata)
for item in self.lizmap_cloud:
item.setVisible(lizmap_cloud)
Expand Down Expand Up @@ -962,6 +969,14 @@ def initGui(self):
# noinspection PyUnresolvedReferences
self.action.triggered.connect(self.run)

self.dock_html_preview = HtmlPreview(None)
self.dock_html_preview.set_server_url(self.dlg.current_server_info(ServerComboData.ServerUrl.value))
self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock_html_preview)
self.dock_html_preview.setVisible(False)
self.dlg.button_maptip_preview.setText('')
self.dlg.button_maptip_preview.setIcon(QIcon(":images/themes/default/mActionShowAllLayers.svg"))
self.dlg.button_maptip_preview.clicked.connect(self.open_dock_preview_maptip)

if self.is_dev_version:
label = 'Lizmap dev'
self.action_debug_pg_qgis = QAction(icon, "Add PostgreSQL connection from datasource")
Expand Down Expand Up @@ -1360,6 +1375,10 @@ def unload(self):
self.iface.webMenu().removeAction(self.action)
self.iface.removeWebToolBarIcon(self.action)

if self.dock_html_preview:
self.iface.removeDockWidget(self.dock_html_preview)
del self.dock_html_preview

if self.help_action:
self.iface.pluginHelpMenu().removeAction(self.help_action)
del self.help_action
Expand All @@ -1378,6 +1397,7 @@ def enable_popup_source_button(self):
data = self.layer_options_list['popupSource']['widget'].currentData()
self.dlg.btConfigurePopup.setVisible(data in ('lizmap', 'qgis'))
self.dlg.widget_qgis_maptip.setVisible(data == 'qgis')
self.dlg.button_maptip_preview.setVisible(data == 'qgis')

if data == 'lizmap':
layer = self._current_selected_layer()
Expand Down Expand Up @@ -4018,6 +4038,13 @@ def on_project_read(self):
self.reinit_default_properties()
self.dlg.close()

def open_dock_preview_maptip(self):
if self.dock_html_preview.isVisible():
return

self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock_html_preview)
self.dock_html_preview.setVisible(True)

def run(self) -> bool:
"""Plugin run method : launch the GUI."""
self.dlg.check_action_file_exists()
Expand Down
6 changes: 6 additions & 0 deletions lizmap/resources/html/maptip_preview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<head>
<link type="text/css" href="{css}" rel="stylesheet" />
</head>
<body>
{maptip}
</body>
7 changes: 7 additions & 0 deletions lizmap/resources/ui/ui_lizmap.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,13 @@ This is different to the map maximum extent (defined in QGIS project properties,
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_maptip_preview">
<property name="text">
<string notr="true">PREVIEW</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
Expand Down