Skip to content

Commit

Permalink
UX - Add a new dock for HTML maptip preview
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Nov 16, 2023
1 parent c1697ef commit 4f5d48e
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 0 deletions.
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

0 comments on commit 4f5d48e

Please sign in to comment.