Graph in eigenes Widget ausgegliedert

This commit is contained in:
Patrick Haßel 2025-09-27 17:49:57 +02:00
parent 844cc90bc2
commit 5a5a45dc7c
10 changed files with 142 additions and 285 deletions

244
Makefile
View File

@ -1,244 +0,0 @@
#/***************************************************************************
# Pegelonline
#
# BlaBlaBla
# -------------------
# begin : 2025-09-26
# git sha : $Format:%H$
# copyright : (C) 2025 by Katrin Haßel
# email : s6kathom@uni-trier.de
# ***************************************************************************/
#
#/***************************************************************************
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU General Public License as published by *
# * the Free Software Foundation; either version 2 of the License, or *
# * (at your option) any later version. *
# * *
# ***************************************************************************/
#################################################
# Edit the following to match your sources lists
#################################################
#Add iso code for any locales you want to support here (space separated)
# default is no locales
# LOCALES = af
LOCALES =
# If locales are enabled, set the name of the lrelease binary on your system. If
# you have trouble compiling the translations, you may have to specify the full path to
# lrelease
#LRELEASE = lrelease
#LRELEASE = lrelease-qt4
# translation
SOURCES = \
__init__.py \
pegelonline.py pegelonline_dockwidget.py
PLUGINNAME = pegelonline
PY_FILES = \
__init__.py \
pegelonline.py pegelonline_dockwidget.py
UI_FILES = pegelonline_dockwidget_base.ui
EXTRAS = metadata.txt icon.png
EXTRA_DIRS =
COMPILED_RESOURCE_FILES = resources.py
PEP8EXCLUDE=pydev,resources.py,conf.py,third_party,ui
# QGISDIR points to the location where your plugin should be installed.
# This varies by platform, relative to your HOME directory:
# * Linux:
# .local/share/QGIS/QGIS3/profiles/default/python/plugins/
# * Mac OS X:
# Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins
# * Windows:
# AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins'
QGISDIR=/home/patrick/.local/share/QGIS/QGIS3/profiles/default/python/plugins/
#################################################
# Normally you would not need to edit below here
#################################################
HELP = help/build/html
PLUGIN_UPLOAD = $(c)/plugin_upload.py
RESOURCE_SRC=$(shell grep '^ *<file' resources.qrc | sed 's@</file>@@g;s/.*>//g' | tr '\n' ' ')
.PHONY: default
default:
@echo While you can use make to build and deploy your plugin, pb_tool
@echo is a much better solution.
@echo A Python script, pb_tool provides platform independent management of
@echo your plugins and runs anywhere.
@echo You can install pb_tool using: pip install pb_tool
@echo See https://g-sherman.github.io/plugin_build_tool/ for info.
compile: $(COMPILED_RESOURCE_FILES)
%.py : %.qrc $(RESOURCES_SRC)
pyrcc5 -o $*.py $<
%.qm : %.ts
$(LRELEASE) $<
test: compile transcompile
@echo
@echo "----------------------"
@echo "Regression Test Suite"
@echo "----------------------"
@# Preceding dash means that make will continue in case of errors
@-export PYTHONPATH=`pwd`:$(PYTHONPATH); \
export QGIS_DEBUG=0; \
export QGIS_LOG_FILE=/dev/null; \
nosetests -v --with-id --with-coverage --cover-package=. \
3>&1 1>&2 2>&3 3>&- || true
@echo "----------------------"
@echo "If you get a 'no module named qgis.core error, try sourcing"
@echo "the helper script we have provided first then run make test."
@echo "e.g. source run-env-linux.sh <path to qgis install>; make test"
@echo "----------------------"
deploy: compile doc transcompile
@echo
@echo "------------------------------------------"
@echo "Deploying plugin to your .qgis2 directory."
@echo "------------------------------------------"
# The deploy target only works on unix like operating system where
# the Python plugin directory is located at:
# $HOME/$(QGISDIR)/python/plugins
mkdir -p $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vf $(PY_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vf $(UI_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vf $(COMPILED_RESOURCE_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vf $(EXTRAS) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vfr i18n $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
cp -vfr $(HELP) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)/help
# Copy extra directories if any
(foreach EXTRA_DIR,(EXTRA_DIRS), cp -R (EXTRA_DIR) (HOME)/(QGISDIR)/python/plugins/(PLUGINNAME)/;)
# The dclean target removes compiled python files from plugin directory
# also deletes any .git entry
dclean:
@echo
@echo "-----------------------------------"
@echo "Removing any compiled python files."
@echo "-----------------------------------"
find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname "*.pyc" -delete
find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname ".git" -prune -exec rm -Rf {} \;
derase:
@echo
@echo "-------------------------"
@echo "Removing deployed plugin."
@echo "-------------------------"
rm -Rf $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
zip: deploy dclean
@echo
@echo "---------------------------"
@echo "Creating plugin zip bundle."
@echo "---------------------------"
# The zip target deploys the plugin and creates a zip file with the deployed
# content. You can then upload the zip file on http://plugins.qgis.org
rm -f $(PLUGINNAME).zip
cd $(HOME)/$(QGISDIR)/python/plugins; zip -9r $(CURDIR)/$(PLUGINNAME).zip $(PLUGINNAME)
package: compile
# Create a zip package of the plugin named $(PLUGINNAME).zip.
# This requires use of git (your plugin development directory must be a
# git repository).
# To use, pass a valid commit or tag as follows:
# make package VERSION=Version_0.3.2
@echo
@echo "------------------------------------"
@echo "Exporting plugin to zip package. "
@echo "------------------------------------"
rm -f $(PLUGINNAME).zip
git archive --prefix=$(PLUGINNAME)/ -o $(PLUGINNAME).zip $(VERSION)
echo "Created package: $(PLUGINNAME).zip"
upload: zip
@echo
@echo "-------------------------------------"
@echo "Uploading plugin to QGIS Plugin repo."
@echo "-------------------------------------"
$(PLUGIN_UPLOAD) $(PLUGINNAME).zip
transup:
@echo
@echo "------------------------------------------------"
@echo "Updating translation files with any new strings."
@echo "------------------------------------------------"
@chmod +x scripts/update-strings.sh
@scripts/update-strings.sh $(LOCALES)
transcompile:
@echo
@echo "----------------------------------------"
@echo "Compiled translation files to .qm files."
@echo "----------------------------------------"
@chmod +x scripts/compile-strings.sh
@scripts/compile-strings.sh $(LRELEASE) $(LOCALES)
transclean:
@echo
@echo "------------------------------------"
@echo "Removing compiled translation files."
@echo "------------------------------------"
rm -f i18n/*.qm
clean:
@echo
@echo "------------------------------------"
@echo "Removing uic and rcc generated files"
@echo "------------------------------------"
rm $(COMPILED_UI_FILES) $(COMPILED_RESOURCE_FILES)
doc:
@echo
@echo "------------------------------------"
@echo "Building documentation using sphinx."
@echo "------------------------------------"
cd help; make html
pylint:
@echo
@echo "-----------------"
@echo "Pylint violations"
@echo "-----------------"
@pylint --reports=n --rcfile=pylintrc . || true
@echo
@echo "----------------------"
@echo "If you get a 'no module named qgis.core' error, try sourcing"
@echo "the helper script we have provided first then run make pylint."
@echo "e.g. source run-env-linux.sh <path to qgis install>; make pylint"
@echo "----------------------"
# Run pep8 style checking
#http://pypi.python.org/pypi/pep8
pep8:
@echo
@echo "-----------"
@echo "PEP8 issues"
@echo "-----------"
@pep8 --repeat --ignore=E203,E121,E122,E123,E124,E125,E126,E127,E128 --exclude $(PEP8EXCLUDE) . || true
@echo "-----------"
@echo "Ignored in PEP8 check:"
@echo $(PEP8EXCLUDE)

View File

@ -1,3 +1,5 @@
Beim hinzufügen von Features fehlte das layer.updateFields()
Beim hinzufügen von Features fehlte wurde versucht eine viel zu große Pegelonline 'number' in einen Int zu stecken. War schwierig zu entdecken => provider.errors()
Bild wird beim "Neu laden" doppelt heruntergeladen
Beim Hinzufügen von Features fehlte das layer.updateFields()
Beim Hinzufügen von Features fehlte wurde versucht eine viel zu große Pegelonline 'number' in einen Int zu stecken. War schwierig zu entdecken => provider.errors()
DockWidget wurde ziemlich voll → Weiteres Widget nur für Grafen
buttons etc disablen

View File

@ -6,10 +6,10 @@ Congratulations! You just built a plugin for QGIS!<br/><br />
<div id='help' style='font-size:.9em;'>
Your plugin <b>Pegelonline</b> was created in:<br>
&nbsp;&nbsp;<b>/home/patrick/PycharmProjects/pegelonline</b>
&nbsp;&nbsp;<b>~/PycharmProjects/pegelonline</b>
<p>
Your QGIS plugin directory is located at:<br>
&nbsp;&nbsp;<b>/home/patrick/.local/share/QGIS/QGIS3/profiles/default/python/plugins</b>
&nbsp;&nbsp;<b>~/.local/share/QGIS/QGIS3/profiles/default/python/plugins</b>
<p>
<h3>What's Next</h3>
<ol>

View File

@ -1,10 +1,10 @@
Plugin Builder Results
Your plugin Pegelonline was created in:
/home/patrick/PycharmProjects/pegelonline
~/PycharmProjects/pegelonline
Your QGIS plugin directory is located at:
/home/patrick/.local/share/QGIS/QGIS3/profiles/default/python/plugins
~/.local/share/QGIS/QGIS3/profiles/default/python/plugins
What's Next:

View File

@ -44,23 +44,23 @@ name: pegelonline
# Full path to where you want your plugin directory copied. If empty,
# the QGIS default path will be used. Don't include the plugin name in
# the path.
plugin_path:/home/patrick/.local/share/QGIS/QGIS3/profiles/default/python/plugins
plugin_path:~/.local/share/QGIS/QGIS3/profiles/default/python/plugins
[files]
# Python files that should be deployed with the plugin
python_files: __init__.py pegelonline.py pegelonline_dockwidget.py po_runner.py
python_files: __init__.py pegelonline.py pegelonline_dockwidget.py pegelonline_dockwidget_graph.py po_runner.py
# The main dialog file that is loaded (not compiled)
main_dialog: pegelonline_dockwidget_base.ui
# Other ui files for dialogs you create (these will be compiled)
compiled_ui_files:
compiled_ui_files:
# Resource file(s) that will be compiled
resource_files: resources.qrc
# Other files required for the plugin
extras: metadata.txt icon.png
extras: metadata.txt icon.png pegelonline_dockwidget_graph.ui
# Other directories to be deployed with the plugin.
# These must be subdirectories under the plugin directory

View File

@ -28,6 +28,7 @@ from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
from .pegelonline_dockwidget import PegelonlineDockWidget
from .pegelonline_dockwidget_graph import PegelonlineDockWidgetGraph
from .po_runner import PoRunner
@ -70,7 +71,9 @@ class Pegelonline:
# print "** INITIALIZING Pegelonline"
self.pluginIsActive = False
self.dockwidget = None
self.main = None
self.graph = None
self.runner = None
# noinspection PyMethodMayBeStatic
def tr(self, message):
@ -178,7 +181,8 @@ class Pegelonline:
# print "** CLOSING Pegelonline"
# disconnects
self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)
self.main.closingPlugin.disconnect(self.onClosePlugin)
self.graph.closingPlugin.disconnect(self.onClosePlugin)
# remove this statement if dockwidget is to remain
# for reuse if plugin is reopened
@ -211,15 +215,18 @@ class Pegelonline:
print("** STARTING Pegelonline")
if self.dockwidget == None:
# Create the dockwidget (after translation) and keep reference
self.dockwidget = PegelonlineDockWidget()
self.runner = PoRunner(self.dockwidget, self.iface)
if self.main is None:
self.main = PegelonlineDockWidget()
self.graph = PegelonlineDockWidgetGraph()
self.runner = PoRunner(self.main, self.graph, self.iface)
# connect to provide cleanup on closing of dockwidget
self.dockwidget.closingPlugin.connect(self.onClosePlugin)
self.main.closingPlugin.connect(self.onClosePlugin)
self.graph.closingPlugin.connect(self.onClosePlugin)
# show the dockwidget
# TODO: fix to allow choice of dock location
self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)
self.dockwidget.show()
# setze positionen
self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.main)
self.iface.addDockWidget(Qt.RightDockWidgetArea, self.graph)
# show
self.main.show()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>301</width>
<height>660</height>
<height>638</height>
</rect>
</property>
<property name="maximumSize">
@ -326,13 +326,6 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lbHistory">
<property name="text">
<string>Bitte wählen...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
PegelonlineDockWidgetGraph
A QGIS plugin
Stellt Pegelstandsverläufe von Pegelonline grafisch dar
Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
-------------------
begin : 2025-09-26
git sha : $Format:%H$
copyright : (C) 2025 by Katrin Haßel
email : s6kathom@uni-trier.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""
import os
from qgis.PyQt import QtWidgets, uic
from qgis.PyQt.QtCore import pyqtSignal
FORM_CLASS, _ = uic.loadUiType(os.path.join(os.path.dirname(__file__), 'pegelonline_dockwidget_graph.ui'))
class PegelonlineDockWidgetGraph(QtWidgets.QDockWidget, FORM_CLASS):
closingPlugin = pyqtSignal()
def __init__(self, parent=None):
"""Constructor."""
super(PegelonlineDockWidgetGraph, self).__init__(parent)
# Set up the user interface from Designer.
# After setupUI you can access any designer object by doing
# self.<objectname>, and you can use autoconnect slots - see
# http://doc.qt.io/qt-5/designer-using-a-ui-file.html
# #widgets-and-dialogs-with-auto-connect
self.setupUi(self)
def closeEvent(self, event):
self.closingPlugin.emit()
event.accept()

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PegelOnlineDockWidgetGraph</class>
<widget class="QDockWidget" name="PegelOnlineDockWidgetGraph">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Pegelonline: Verlauf</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lbHistory">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Bitte Station auswählen...</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -4,6 +4,8 @@ from typing import Callable
from PyQt5 import QtGui
from qgis._core import QgsVectorLayer, QgsProject, QgsLayerTreeLayer, QgsPalLayerSettings, QgsVectorLayerSimpleLabeling
from .pegelonline_dockwidget import PegelonlineDockWidget
from .pegelonline_dockwidget_graph import PegelonlineDockWidgetGraph
from .pomodules.po_history import PoHistory
from .pomodules.po_stations import PoStations
from .pomodules.po_stations_qgs import PoStationsQgs
@ -13,11 +15,16 @@ from .pomodules.po_waterlevels_qgs import PoWaterlevelsQgs
# noinspection PyMethodMayBeStatic
class PoRunner(object):
def __init__(self, ui, iface):
def __init__(self, ui: PegelonlineDockWidget, graph: PegelonlineDockWidgetGraph, iface):
self.ui = ui
self.graph = graph
self.iface = iface
self.local_dir = os.path.dirname(os.path.realpath(__file__))
# Sperre für Pegelverlauf-Stationsliste
self._history_stations_lock = True
# Layer
self.stations = None
self.waterlevels = None
@ -30,8 +37,6 @@ class PoRunner(object):
self._waterlevels_connect_signals()
self._history_connect_signals()
self._history_load_stations()
# basemap -----------------------------------------------------------------
def _basemap_create(self, path, name, disconnect: Callable[[], None]) -> None | QgsVectorLayer:
@ -167,7 +172,7 @@ class PoRunner(object):
if self.ui.cbStationsWater.isChecked():
fields.append('"water"')
if self.ui.cbStationsKm.isChecked():
fields.append('"km", \' km\'') # 2 Teile anhängen: km, " km"
fields.append('"km", \' km\'') # 2 Teile anhängen: km, " km"
self._layer_update_labels(self.stations, fields)
@ -263,7 +268,7 @@ class PoRunner(object):
if self.ui.cbWaterlevelsTimestamp.isChecked():
fields.append('"timestamp"')
if self.ui.cbWaterlevelsValue.isChecked():
fields.append('"value", \' \', "unit"') # 3 Teile anhängen: value, leerzeichen, unit
fields.append('"value", \' \', "unit"') # 3 Teile anhängen: value, leerzeichen, unit
if self.ui.cbWaterlevelsMean.isChecked():
fields.append('\'MnwMhw=\', "stateMnwMhw"')
if self.ui.cbWaterlevelsAbsolute.isChecked():
@ -368,21 +373,28 @@ class PoRunner(object):
def _history_load_graph(self):
print("_history_load_graph")
if self._history_stations_lock:
print("_history_load_graph: Stationsliste ist aktuell gesperrt")
return
station = self.ui.slHistoryStation.currentText()
days = self.ui.numHistoryDays.value()
print("_history_load_graph: station=%s days=%s" % (station, days))
self.ui.lbHistory.clear()
self.graph.lbHistory.clear()
self.graph.setWindowTitle("%s / %d Tag(e)" % (station, days))
self.graph.show()
if station == '' or station is None:
print("_history_load_graph: Fehler: Ungültige Station: %s" % (station,))
self.ui.lbHistory.setText("Bitte Station wählen...")
self.graph.lbHistory.setText("Bitte Station wählen...")
return
if days is None or days < 1 or days > 30:
print("_history_load_graph: Fehler: Ungültige Anzahl von Tagen: %s" % (days,))
self.ui.lbHistory.setText("Bitte Tage [1, 30] wählen...")
self.graph.lbHistory.setText("Bitte Tage [1, 30] wählen...")
return
history = PoHistory(station, days)
@ -390,18 +402,19 @@ class PoRunner(object):
if image_data is None or len(image_data) == 0:
print("_history_load_graph: Fehler: Keine Daten erhalten")
self.ui.lbHistory.setText("Fehler beim Download!")
self.graph.lbHistory.setText("Fehler beim Download!")
return
pixmap = QtGui.QPixmap()
pixmap.loadFromData(image_data)
self.ui.lbHistory.setPixmap(pixmap)
self.ui.lbHistory.resize(pixmap.width(), pixmap.height())
self.graph.lbHistory.setPixmap(pixmap)
self.graph.lbHistory.resize(pixmap.width(), pixmap.height())
print("_history_load_graph: Bild erfolgreich geladen")
def _history_load_stations(self):
print("_history_load_stations")
self._history_stations_lock = True
# behalte die aktuelle Station, um sie (mit eventuell neuem Index) wiederherzustellen
current_station = self.ui.slHistoryStation.currentText()
@ -430,3 +443,5 @@ class PoRunner(object):
self.ui.slHistoryStation.setCurrentIndex(0)
station = self.ui.slHistoryStation.currentText()
print("_history_load_stations: Bisherige Station \"%s\" nicht wiedergefunden. Nehme erste Station: %s" % (current_station, station))
self._history_stations_lock = False