From b899c160a439614b9058ad36eb7f8d7befa15ed8 Mon Sep 17 00:00:00 2001 From: Fintan O'Sullivan Date: Tue, 8 Dec 2020 15:40:47 +1300 Subject: [PATCH] First hello world plugin --- helloworld/Makefile | 244 ++++++++++++++++++ helloworld/README.html | 42 +++ helloworld/README.txt | 32 +++ helloworld/__init__.py | 36 +++ helloworld/compile.bat | 7 + helloworld/helloworld.py | 200 +++++++++++++++ helloworld/helloworld_dialog.py | 44 ++++ helloworld/helloworld_dialog_base.ui | 67 +++++ helloworld/help/Makefile | 130 ++++++++++ helloworld/help/make.bat | 155 ++++++++++++ helloworld/help/source/conf.py | 216 ++++++++++++++++ helloworld/help/source/index.rst | 20 ++ helloworld/i18n/af.ts | 11 + helloworld/icon.png | Bin 0 -> 1034 bytes helloworld/metadata.txt | 47 ++++ helloworld/pb_tool.cfg | 80 ++++++ helloworld/plugin_upload.py | 111 ++++++++ helloworld/pylintrc | 281 +++++++++++++++++++++ helloworld/resources.py | 128 ++++++++++ helloworld/resources.qrc | 5 + helloworld/scripts/compile-strings.sh | 12 + helloworld/scripts/run-env-linux.sh | 28 ++ helloworld/scripts/update-strings.sh | 56 ++++ helloworld/test/__init__.py | 2 + helloworld/test/qgis_interface.py | 205 +++++++++++++++ helloworld/test/tenbytenraster.asc | 19 ++ helloworld/test/tenbytenraster.asc.aux.xml | 13 + helloworld/test/tenbytenraster.keywords | 1 + helloworld/test/tenbytenraster.lic | 18 ++ helloworld/test/tenbytenraster.prj | 1 + helloworld/test/tenbytenraster.qml | 26 ++ helloworld/test/test_helloworld_dialog.py | 55 ++++ helloworld/test/test_init.py | 64 +++++ helloworld/test/test_qgis_environment.py | 60 +++++ helloworld/test/test_resources.py | 44 ++++ helloworld/test/test_translations.py | 55 ++++ helloworld/test/utilities.py | 61 +++++ 37 files changed, 2576 insertions(+) create mode 100644 helloworld/Makefile create mode 100644 helloworld/README.html create mode 100644 helloworld/README.txt create mode 100644 helloworld/__init__.py create mode 100644 helloworld/compile.bat create mode 100644 helloworld/helloworld.py create mode 100644 helloworld/helloworld_dialog.py create mode 100644 helloworld/helloworld_dialog_base.ui create mode 100644 helloworld/help/Makefile create mode 100644 helloworld/help/make.bat create mode 100644 helloworld/help/source/conf.py create mode 100644 helloworld/help/source/index.rst create mode 100644 helloworld/i18n/af.ts create mode 100644 helloworld/icon.png create mode 100644 helloworld/metadata.txt create mode 100644 helloworld/pb_tool.cfg create mode 100644 helloworld/plugin_upload.py create mode 100644 helloworld/pylintrc create mode 100644 helloworld/resources.py create mode 100644 helloworld/resources.qrc create mode 100644 helloworld/scripts/compile-strings.sh create mode 100644 helloworld/scripts/run-env-linux.sh create mode 100644 helloworld/scripts/update-strings.sh create mode 100644 helloworld/test/__init__.py create mode 100644 helloworld/test/qgis_interface.py create mode 100644 helloworld/test/tenbytenraster.asc create mode 100644 helloworld/test/tenbytenraster.asc.aux.xml create mode 100644 helloworld/test/tenbytenraster.keywords create mode 100644 helloworld/test/tenbytenraster.lic create mode 100644 helloworld/test/tenbytenraster.prj create mode 100644 helloworld/test/tenbytenraster.qml create mode 100644 helloworld/test/test_helloworld_dialog.py create mode 100644 helloworld/test/test_init.py create mode 100644 helloworld/test/test_qgis_environment.py create mode 100644 helloworld/test/test_resources.py create mode 100644 helloworld/test/test_translations.py create mode 100644 helloworld/test/utilities.py diff --git a/helloworld/Makefile b/helloworld/Makefile new file mode 100644 index 0000000..4e901ee --- /dev/null +++ b/helloworld/Makefile @@ -0,0 +1,244 @@ +#/*************************************************************************** +# helloWorld +# +# Simple hello world plugin +# ------------------- +# begin : 2020-12-07 +# git sha : $Format:%H$ +# copyright : (C) 2020 by Fintan O'Sullivan +# email : fintanosullivan14@gmail.com +# ***************************************************************************/ +# +#/*************************************************************************** +# * * +# * 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 \ + helloworld.py helloworld_dialog.py + +PLUGINNAME = helloworld + +PY_FILES = \ + __init__.py \ + helloworld.py helloworld_dialog.py + +UI_FILES = helloworld_dialog_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=C:\Users\Fintan\AppData/Roaming/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 '^ *@@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 ; 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 ; 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) diff --git a/helloworld/README.html b/helloworld/README.html new file mode 100644 index 0000000..1f6e915 --- /dev/null +++ b/helloworld/README.html @@ -0,0 +1,42 @@ + + +

Plugin Builder Results

+ +Congratulations! You just built a plugin for QGIS!

+ +
+Your plugin helloWorld was created in:
+  C:/Users/Fintan/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\helloworld +

+Your QGIS plugin directory is located at:
+  C:/Users/Fintan/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins +

+

What's Next

+
    +
  1. If resources.py is not present in your plugin directory, compile the resources file using pyrcc5 (simply use pb_tool or make if you have automake) +
  2. Optionally, test the generated sources using make test (or run tests from your IDE) +
  3. Copy the entire directory containing your new plugin to the QGIS plugin directory (see Notes below) +
  4. Test the plugin by enabling it in the QGIS plugin manager +
  5. Customize it by editing the implementation file helloworld.py +
  6. Create your own custom icon, replacing the default icon.png +
  7. Modify your user interface by opening helloworld_dialog_base.ui in Qt Designer +
+Notes: +
    +
  • You can use pb_tool to compile, deploy, and manage your plugin. Tweak the pb_tool.cfg file included with your plugin as you add files. Install pb_tool using + pip or easy_install. See http://loc8.cc/pb_tool for more information. +
  • You can also use the Makefile to compile and deploy when you + make changes. This requires GNU make (gmake). The Makefile is ready to use, however you + will have to edit it to add addional Python source files, dialogs, and translations. +
+
+
+

+For information on writing PyQGIS code, see http://loc8.cc/pyqgis_resources for a list of resources. +

+
+

+©2011-2019 GeoApt LLC - geoapt.com +

+ + diff --git a/helloworld/README.txt b/helloworld/README.txt new file mode 100644 index 0000000..73a8d31 --- /dev/null +++ b/helloworld/README.txt @@ -0,0 +1,32 @@ +Plugin Builder Results + +Your plugin helloWorld was created in: + C:/Users/Fintan/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins\helloworld + +Your QGIS plugin directory is located at: + C:/Users/Fintan/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins + +What's Next: + + * Copy the entire directory containing your new plugin to the QGIS plugin + directory + + * Compile the resources file using pyrcc5 + + * Run the tests (``make test``) + + * Test the plugin by enabling it in the QGIS plugin manager + + * Customize it by editing the implementation file: ``helloworld.py`` + + * Create your own custom icon, replacing the default icon.png + + * Modify your user interface by opening helloWorld_dialog_base.ui in Qt Designer + + * You can use the Makefile to compile your Ui and resource files when + you make changes. This requires GNU make (gmake) + +For more information, see the PyQGIS Developer Cookbook at: +http://www.qgis.org/pyqgis-cookbook/index.html + +(C) 2011-2018 GeoApt LLC - geoapt.com diff --git a/helloworld/__init__.py b/helloworld/__init__.py new file mode 100644 index 0000000..77b9d1b --- /dev/null +++ b/helloworld/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + helloWorld + A QGIS plugin + Simple hello world plugin + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2020-12-07 + copyright : (C) 2020 by Fintan O'Sullivan + email : fintanosullivan14@gmail.com + git sha : $Format:%H$ + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + This script initializes the plugin, making it known to QGIS. +""" + + +# noinspection PyPep8Naming +def classFactory(iface): # pylint: disable=invalid-name + """Load helloWorld class from file helloWorld. + + :param iface: A QGIS interface instance. + :type iface: QgsInterface + """ + # + from .helloworld import helloWorld + return helloWorld(iface) diff --git a/helloworld/compile.bat b/helloworld/compile.bat new file mode 100644 index 0000000..be35344 --- /dev/null +++ b/helloworld/compile.bat @@ -0,0 +1,7 @@ +@echo off +call "C:\Program Files\QGIS 3.16\bin\o4w_env.bat" +call "C:\Program Files\QGIS 3.16\bin\qt5_env.bat" +call "C:\Program Files\QGIS 3.16\bin\py3_env.bat" + +@echo on +pyrcc5 -o resources.py resources.qrc \ No newline at end of file diff --git a/helloworld/helloworld.py b/helloworld/helloworld.py new file mode 100644 index 0000000..e64eedc --- /dev/null +++ b/helloworld/helloworld.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + helloWorld + A QGIS plugin + Simple hello world plugin + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2020-12-07 + git sha : $Format:%H$ + copyright : (C) 2020 by Fintan O'Sullivan + email : fintanosullivan14@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ +""" +from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication +from qgis.PyQt.QtGui import QIcon +from qgis.PyQt.QtWidgets import QAction + +# Initialize Qt resources from file resources.py +from .resources import * +# Import the code for the dialog +from .helloworld_dialog import helloWorldDialog +import os.path + + +class helloWorld: + """QGIS Plugin Implementation.""" + + def __init__(self, iface): + """Constructor. + + :param iface: An interface instance that will be passed to this class + which provides the hook by which you can manipulate the QGIS + application at run time. + :type iface: QgsInterface + """ + # Save reference to the QGIS interface + self.iface = iface + # initialize plugin directory + self.plugin_dir = os.path.dirname(__file__) + # initialize locale + locale = QSettings().value('locale/userLocale')[0:2] + locale_path = os.path.join( + self.plugin_dir, + 'i18n', + 'helloWorld_{}.qm'.format(locale)) + + if os.path.exists(locale_path): + self.translator = QTranslator() + self.translator.load(locale_path) + QCoreApplication.installTranslator(self.translator) + + # Declare instance attributes + self.actions = [] + self.menu = self.tr(u'&Hello World') + + # Check if plugin was started the first time in current QGIS session + # Must be set in initGui() to survive plugin reloads + self.first_start = None + + # noinspection PyMethodMayBeStatic + def tr(self, message): + """Get the translation for a string using Qt translation API. + + We implement this ourselves since we do not inherit QObject. + + :param message: String for translation. + :type message: str, QString + + :returns: Translated version of message. + :rtype: QString + """ + # noinspection PyTypeChecker,PyArgumentList,PyCallByClass + return QCoreApplication.translate('helloWorld', message) + + + def add_action( + self, + icon_path, + text, + callback, + enabled_flag=True, + add_to_menu=True, + add_to_toolbar=True, + status_tip=None, + whats_this=None, + parent=None): + """Add a toolbar icon to the toolbar. + + :param icon_path: Path to the icon for this action. Can be a resource + path (e.g. ':/plugins/foo/bar.png') or a normal file system path. + :type icon_path: str + + :param text: Text that should be shown in menu items for this action. + :type text: str + + :param callback: Function to be called when the action is triggered. + :type callback: function + + :param enabled_flag: A flag indicating if the action should be enabled + by default. Defaults to True. + :type enabled_flag: bool + + :param add_to_menu: Flag indicating whether the action should also + be added to the menu. Defaults to True. + :type add_to_menu: bool + + :param add_to_toolbar: Flag indicating whether the action should also + be added to the toolbar. Defaults to True. + :type add_to_toolbar: bool + + :param status_tip: Optional text to show in a popup when mouse pointer + hovers over the action. + :type status_tip: str + + :param parent: Parent widget for the new action. Defaults None. + :type parent: QWidget + + :param whats_this: Optional text to show in the status bar when the + mouse pointer hovers over the action. + + :returns: The action that was created. Note that the action is also + added to self.actions list. + :rtype: QAction + """ + + icon = QIcon(icon_path) + action = QAction(icon, text, parent) + action.triggered.connect(callback) + action.setEnabled(enabled_flag) + + if status_tip is not None: + action.setStatusTip(status_tip) + + if whats_this is not None: + action.setWhatsThis(whats_this) + + if add_to_toolbar: + # Adds plugin icon to Plugins toolbar + self.iface.addToolBarIcon(action) + + if add_to_menu: + self.iface.addPluginToMenu( + self.menu, + action) + + self.actions.append(action) + + return action + + def initGui(self): + """Create the menu entries and toolbar icons inside the QGIS GUI.""" + + icon_path = ':/plugins/helloworld/icon.png' + self.add_action( + icon_path, + text=self.tr(u'Hello World'), + callback=self.run, + parent=self.iface.mainWindow()) + + # will be set False in run() + self.first_start = True + + + def unload(self): + """Removes the plugin menu item and icon from QGIS GUI.""" + for action in self.actions: + self.iface.removePluginMenu( + self.tr(u'&Hello World'), + action) + self.iface.removeToolBarIcon(action) + + + def run(self): + """Run method that performs all the real work""" + + # Create the dialog with elements (after translation) and keep reference + # Only create GUI ONCE in callback, so that it will only load when the plugin is started + if self.first_start == True: + self.first_start = False + self.dlg = helloWorldDialog() + + # show the dialog + self.dlg.show() + # Run the dialog event loop + result = self.dlg.exec_() + # See if OK was pressed + if result: + # Do something useful here - delete the line containing pass and + # substitute with your code. + pass diff --git a/helloworld/helloworld_dialog.py b/helloworld/helloworld_dialog.py new file mode 100644 index 0000000..aef9803 --- /dev/null +++ b/helloworld/helloworld_dialog.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + helloWorldDialog + A QGIS plugin + Simple hello world plugin + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2020-12-07 + git sha : $Format:%H$ + copyright : (C) 2020 by Fintan O'Sullivan + email : fintanosullivan14@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 uic +from qgis.PyQt import QtWidgets + +# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer +FORM_CLASS, _ = uic.loadUiType(os.path.join( + os.path.dirname(__file__), 'helloworld_dialog_base.ui')) + + +class helloWorldDialog(QtWidgets.QDialog, FORM_CLASS): + def __init__(self, parent=None): + """Constructor.""" + super(helloWorldDialog, self).__init__(parent) + # Set up the user interface from Designer through FORM_CLASS. + # After self.setupUi() you can access any designer object by doing + # self., and you can use autoconnect slots - see + # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html + # #widgets-and-dialogs-with-auto-connect + self.setupUi(self) diff --git a/helloworld/helloworld_dialog_base.ui b/helloworld/helloworld_dialog_base.ui new file mode 100644 index 0000000..3e06e51 --- /dev/null +++ b/helloworld/helloworld_dialog_base.ui @@ -0,0 +1,67 @@ + + helloWorldDialogBase + + + + 0 + 0 + 400 + 300 + + + + Hello World + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + button_box + accepted() + helloWorldDialogBase + accept() + + + 248 + 254 + + + 157 + 274 + + + + + button_box + rejected() + helloWorldDialogBase + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/helloworld/help/Makefile b/helloworld/help/Makefile new file mode 100644 index 0000000..9def777 --- /dev/null +++ b/helloworld/help/Makefile @@ -0,0 +1,130 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/template_class.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/template_class.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/template_class" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/template_class" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/helloworld/help/make.bat b/helloworld/help/make.bat new file mode 100644 index 0000000..3377610 --- /dev/null +++ b/helloworld/help/make.bat @@ -0,0 +1,155 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\template_class.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\template_class.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/helloworld/help/source/conf.py b/helloworld/help/source/conf.py new file mode 100644 index 0000000..f056f93 --- /dev/null +++ b/helloworld/help/source/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# helloWorld documentation build configuration file, created by +# sphinx-quickstart on Sun Feb 12 17:11:03 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.todo', 'sphinx.ext.imgmath', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'helloWorld' +copyright = u'2013, Fintan O'Sullivan' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_TemplateModuleNames = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'TemplateClassdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'helloWorld.tex', u'helloWorld Documentation', + u'Fintan O'Sullivan', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'TemplateClass', u'helloWorld Documentation', + [u'Fintan O'Sullivan'], 1) +] diff --git a/helloworld/help/source/index.rst b/helloworld/help/source/index.rst new file mode 100644 index 0000000..d0bbf5e --- /dev/null +++ b/helloworld/help/source/index.rst @@ -0,0 +1,20 @@ +.. helloWorld documentation master file, created by + sphinx-quickstart on Sun Feb 12 17:11:03 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to helloWorld's documentation! +============================================ + +Contents: + +.. toctree:: + :maxdepth: 2 + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/helloworld/i18n/af.ts b/helloworld/i18n/af.ts new file mode 100644 index 0000000..615a88c --- /dev/null +++ b/helloworld/i18n/af.ts @@ -0,0 +1,11 @@ + + + + @default + + + Good morning + Goeie more + + + diff --git a/helloworld/icon.png b/helloworld/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f696c00303219cfcc2f0d57608d04e298fbf56c3 GIT binary patch literal 1034 zcmV+l1oiugP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXD@ z77;9+NQ1io00W9iL_t(Y$E}r1Xk1ko$A9PCduQg(OlBrY(9Ks7iMEcmzAKc3;VzZCAM*45PN_;^e6>czpi>a)k;jWgn$XwX#(ffW0x= z+8`f&KFz}YS*DXYlF=gSn-KxzBG6N#o}Sbu5ylW$ifX;y!Gxcoccb7X?l*&Cg%BLp+upi(AsPbFBN>WXDC?; z&(>7p1`~%XZ)m0`uQ4(<$FhGH?|poXGiT2da3o4;)@nx9({OUBf@d^Etti_X6*QRt zM(yu64Vk}6JTN%G$V;!1TAbqGBW-lFx`bT|DMfv_l!BJF5c985o@kjA(}*tNFK{QCU>`#$u@1vR=}>qa5@&kCuGhvP`xkhN}@YN}=O zQj$Y2Uc!t58W8mdtiyG1gOY%=`Oqf#kFzPpKDdk-HFaxyemu%W41P&5^jJIh_B4=) zIRqS8AO73bJYStlGW|rdy4U))V9(wb+>k{~N-CaW{!Ycn!A{zChUn=E(`kQe>l33Is7?7;qutHE1STpm8YoYyRmhySVLwneD zILtlW(W=jJRx_{aH|L?0Ar`gK1}rHy^>Uk*mB7T>op^&oQzC$62^{-|!2sta za1=;y9wJfWb=kJ1ful09`}7PjxUqtpunAx=vHSGQzo6-Eu3FzqfdBvi07*qoM6N<$ Ef=x2zx&QzG literal 0 HcmV?d00001 diff --git a/helloworld/metadata.txt b/helloworld/metadata.txt new file mode 100644 index 0000000..3e044bd --- /dev/null +++ b/helloworld/metadata.txt @@ -0,0 +1,47 @@ +# This file contains metadata for your plugin. + +# This file should be included when you package your plugin.# Mandatory items: + +[general] +name=Hello World +qgisMinimumVersion=3.0 +description=Simple hello world plugin +version=0.1 +author=Fintan O'Sullivan +email=fintanosullivan14@gmail.com + +about=Simple hello world plugin. + +tracker=https://github.com/FoldingSpace/ptobler-qgis-plugin +repository=https://github.com/FoldingSpace/ptobler-qgis-plugin +# End of mandatory metadata + +# Recommended items: + +hasProcessingProvider=no +# Uncomment the following line and add your changelog: +# changelog= + +# Tags are comma separated with spaces allowed +tags=python + +homepage= +category=Plugins +icon=icon.png +# experimental flag +experimental=True + +# deprecated flag (applies to the whole plugin, not just a single version) +deprecated=False + +# Since QGIS 3.8, a comma separated list of plugins to be installed +# (or upgraded) can be specified. +# Check the documentation for more information. +# plugin_dependencies= + +Category of the plugin: Raster, Vector, Database or Web +# category= + +# If the plugin can run on QGIS Server. +server=False + diff --git a/helloworld/pb_tool.cfg b/helloworld/pb_tool.cfg new file mode 100644 index 0000000..8e42003 --- /dev/null +++ b/helloworld/pb_tool.cfg @@ -0,0 +1,80 @@ +#/*************************************************************************** +# helloWorld +# +# Configuration file for plugin builder tool (pb_tool) +# Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ +# ------------------- +# begin : 2020-12-07 +# copyright : (C) 2020 by Fintan O'Sullivan +# email : fintanosullivan14@gmail.com +# ***************************************************************************/ +# +#/*************************************************************************** +# * * +# * 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. * +# * * +# ***************************************************************************/ +# +# +# You can install pb_tool using: +# pip install http://geoapt.net/files/pb_tool.zip +# +# Consider doing your development (and install of pb_tool) in a virtualenv. +# +# For details on setting up and using pb_tool, see: +# http://g-sherman.github.io/plugin_build_tool/ +# +# Issues and pull requests here: +# https://github.com/g-sherman/plugin_build_tool: +# +# Sane defaults for your plugin generated by the Plugin Builder are +# already set below. +# +# As you add Python source files and UI files to your plugin, add +# them to the appropriate [files] section below. + +[plugin] +# Name of the plugin. This is the name of the directory that will +# be created in .qgis2/python/plugins +name: helloworld + +# 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: + +[files] +# Python files that should be deployed with the plugin +python_files: __init__.py helloworld.py helloworld_dialog.py + +# The main dialog file that is loaded (not compiled) +main_dialog: helloworld_dialog_base.ui + +# Other ui files for dialogs you create (these will be compiled) +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 + +# Other directories to be deployed with the plugin. +# These must be subdirectories under the plugin directory +extra_dirs: + +# ISO code(s) for any locales (translations), separated by spaces. +# Corresponding .ts files must exist in the i18n directory +locales: + +[help] +# the built help directory that should be deployed with the plugin +dir: help/build/html +# the name of the directory to target in the deployed plugin +target: help + + + diff --git a/helloworld/plugin_upload.py b/helloworld/plugin_upload.py new file mode 100644 index 0000000..a88ea2b --- /dev/null +++ b/helloworld/plugin_upload.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# coding=utf-8 +"""This script uploads a plugin package to the plugin repository. + Authors: A. Pasotti, V. Picavet + git sha : $TemplateVCSFormat +""" + +import sys +import getpass +import xmlrpc.client +from optparse import OptionParser + +standard_library.install_aliases() + +# Configuration +PROTOCOL = 'https' +SERVER = 'plugins.qgis.org' +PORT = '443' +ENDPOINT = '/plugins/RPC2/' +VERBOSE = False + + +def main(parameters, arguments): + """Main entry point. + + :param parameters: Command line parameters. + :param arguments: Command line arguments. + """ + address = "{protocol}://{username}:{password}@{server}:{port}{endpoint}".format( + protocol=PROTOCOL, + username=parameters.username, + password=parameters.password, + server=parameters.server, + port=parameters.port, + endpoint=ENDPOINT) + print("Connecting to: %s" % hide_password(address)) + + server = xmlrpc.client.ServerProxy(address, verbose=VERBOSE) + + try: + with open(arguments[0], 'rb') as handle: + plugin_id, version_id = server.plugin.upload( + xmlrpc.client.Binary(handle.read())) + print("Plugin ID: %s" % plugin_id) + print("Version ID: %s" % version_id) + except xmlrpc.client.ProtocolError as err: + print("A protocol error occurred") + print("URL: %s" % hide_password(err.url, 0)) + print("HTTP/HTTPS headers: %s" % err.headers) + print("Error code: %d" % err.errcode) + print("Error message: %s" % err.errmsg) + except xmlrpc.client.Fault as err: + print("A fault occurred") + print("Fault code: %d" % err.faultCode) + print("Fault string: %s" % err.faultString) + + +def hide_password(url, start=6): + """Returns the http url with password part replaced with '*'. + + :param url: URL to upload the plugin to. + :type url: str + + :param start: Position of start of password. + :type start: int + """ + start_position = url.find(':', start) + 1 + end_position = url.find('@') + return "%s%s%s" % ( + url[:start_position], + '*' * (end_position - start_position), + url[end_position:]) + + +if __name__ == "__main__": + parser = OptionParser(usage="%prog [options] plugin.zip") + parser.add_option( + "-w", "--password", dest="password", + help="Password for plugin site", metavar="******") + parser.add_option( + "-u", "--username", dest="username", + help="Username of plugin site", metavar="user") + parser.add_option( + "-p", "--port", dest="port", + help="Server port to connect to", metavar="80") + parser.add_option( + "-s", "--server", dest="server", + help="Specify server name", metavar="plugins.qgis.org") + options, args = parser.parse_args() + if len(args) != 1: + print("Please specify zip file.\n") + parser.print_help() + sys.exit(1) + if not options.server: + options.server = SERVER + if not options.port: + options.port = PORT + if not options.username: + # interactive mode + username = getpass.getuser() + print("Please enter user name [%s] :" % username, end=' ') + + res = input() + if res != "": + options.username = res + else: + options.username = username + if not options.password: + # interactive mode + options.password = getpass.getpass() + main(options, args) diff --git a/helloworld/pylintrc b/helloworld/pylintrc new file mode 100644 index 0000000..7e168f6 --- /dev/null +++ b/helloworld/pylintrc @@ -0,0 +1,281 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# see http://stackoverflow.com/questions/21487025/pylint-locally-defined-disables-still-give-warnings-how-to-suppress-them +disable=locally-disabled,C0103 + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct attribute names in class +# bodies +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=__.*__ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=no + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/helloworld/resources.py b/helloworld/resources.py new file mode 100644 index 0000000..f526dfd --- /dev/null +++ b/helloworld/resources.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.11.2) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x04\x0a\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x17\x00\x00\x00\x18\x08\x06\x00\x00\x00\x11\x7c\x66\x75\ +\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ +\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ +\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd9\x02\x15\ +\x16\x11\x2c\x9d\x48\x83\xbb\x00\x00\x03\x8a\x49\x44\x41\x54\x48\ +\xc7\xad\x95\x4b\x68\x5c\x55\x18\xc7\x7f\xe7\xdc\x7b\x67\xe6\xce\ +\x4c\x66\x26\x49\xd3\x24\x26\xa6\xc6\xf8\x40\x21\xa5\x04\xb3\x28\ +\xda\x98\x20\xa5\x0b\xad\x55\xa8\x2b\xc5\x50\x1f\xa0\x6e\x34\x2b\ +\x45\x30\x14\x02\xba\x52\x69\x15\x17\x66\x63\x45\x97\x95\xa0\xad\ +\x0b\xfb\xc0\x06\x25\xb6\x71\x61\x12\x41\x50\xdb\x2a\x21\xd1\xe2\ +\x24\xf3\x9e\xc9\xcc\xbd\xe7\x1c\x17\x35\x43\x1e\x33\x21\xb6\xfd\ +\x56\x87\xf3\x9d\xfb\xfb\x1e\xf7\xff\x9d\x23\x8c\x31\x43\x95\xf4\ +\x85\x1e\x3f\x3b\x35\xac\xfd\xcc\x43\xdc\xa4\x49\x3b\xfe\x9d\x1d\ +\xdb\x7b\x22\x90\x78\xf8\xb2\x28\xa7\xbe\x7d\xc1\x4b\x9d\x79\xdf\ +\x18\x15\xe5\x16\x99\x10\x56\xde\x69\xdc\x3f\x22\xfd\xec\xd4\xf0\ +\xad\x04\x03\x18\xa3\xa2\x7e\x76\x6a\x58\xde\x68\x2b\xb4\x36\xf8\ +\xbe\xc6\x18\x53\xdb\xef\xe7\xfa\xec\xed\x67\x63\x10\x42\x00\xf0\ +\xfb\xd5\x65\x2a\x15\x45\xc7\x6d\x0d\x00\xc4\xa2\xc1\xaa\x6f\x0d\ +\x3e\x6c\xab\xc2\x1c\x56\xa4\x77\x4b\xb0\xf2\x35\x15\x5f\x21\x85\ +\xe0\xc8\x6b\x5f\x92\x2d\x37\x33\x39\xf9\x03\x27\x8e\x1f\xa2\xf7\ +\xbe\x9d\x04\x1c\x0b\x37\xe4\xac\xff\xa6\x30\x87\xbd\xba\x00\x6a\ +\x06\x79\xe5\xf5\xaf\x89\xd9\x92\xc5\xcc\x0a\xd9\x7c\x19\xcf\xe9\ +\xe2\xe4\xa9\x2f\x78\x7c\xff\x01\x72\x85\x0a\x2b\x65\x1f\xa5\x4c\ +\xb5\xb2\x55\x16\x80\xbd\x31\xda\xda\x20\x1f\x7d\x3e\xcd\xc2\xfd\ +\x59\xa6\x93\x39\x92\xd1\x22\xea\x9b\x16\xce\x9d\x3f\xce\xe0\x83\ +\x03\x24\x82\x59\x3a\xdb\x7b\x88\xc7\x82\x68\x63\x58\xc9\xcc\x62\ +\x8c\x21\x18\xb0\x6a\xc3\x37\x06\x49\x16\xff\x24\x6b\xa5\x49\xbb\ +\x25\xbc\xa2\xa6\x21\xbb\x40\x7f\xdf\x00\x83\xbd\x01\x8e\x3c\xd5\ +\x45\xd7\x8e\x6b\x9c\x9c\x98\x25\x1a\xb6\xe8\xbe\x3d\xc2\xdd\x77\ +\x44\x48\xc4\x1c\x22\xe1\xeb\x58\x59\xaf\xcf\xd3\x33\x29\x2e\x34\ +\x2d\x91\x93\x3e\xbe\x34\x78\x01\xc5\xe2\x61\xc5\xae\x72\x8e\x70\ +\xc8\xc2\x0d\x5a\xbc\xf5\xee\x2f\x9c\xfa\x3e\x86\x69\x7a\x8e\xcf\ +\x26\xe6\xf9\x63\xa1\x44\xa1\xa4\xd0\xda\x6c\x0d\x2f\x15\x7c\xb4\ +\x67\x28\x59\x0a\xcf\xd6\x54\xe2\x06\x13\x87\x2b\x6f\x68\xa6\x27\ +\xaf\x31\x32\x36\xc7\xb2\x7f\x17\xef\x7d\x7c\x8c\x33\x67\xcf\x12\ +\x70\x24\x4a\x69\xd6\x6a\x46\xd6\xd3\x70\x72\xa9\x82\x67\x34\x45\ +\xad\x28\xdb\x1a\x15\x34\x98\xff\x46\xed\xef\x37\x0d\x99\xbf\x4a\ +\x3c\x30\x38\xc0\xc8\x4b\xaf\x92\x5a\x9c\xe2\xe0\x23\x6d\x74\xb4\ +\xba\x84\x5d\x0b\x29\x45\x7d\xb8\x94\x82\x96\xb6\x10\xf3\xc5\x12\ +\x2a\xef\x53\x11\x1a\x63\xad\x3f\x93\x19\x85\xf1\xb1\x77\x58\x5a\ +\xf8\x99\x97\x9f\xe9\xa6\x75\x47\x90\xc6\xb8\x43\xd8\xb5\xb6\xce\ +\xfc\xfa\xfd\x00\xfb\x3e\xf4\xc8\x05\x35\xba\x5e\xeb\x46\x21\xf9\ +\xcf\x0a\xa9\x8c\x87\xe3\x48\xdc\x90\xb5\x6e\x98\x6a\xaa\x65\xf2\ +\x52\x92\x43\x2f\x5e\xc2\x8c\x02\x1a\x10\xf5\x07\xac\xc3\x75\x70\ +\x83\x92\x80\xb3\xf9\xd0\x26\xf8\x8f\xb3\x29\xc6\x3e\xb8\x8c\x19\ +\x35\x75\x6b\x7b\x7e\x3c\xca\x45\x0c\x7e\x49\x31\xf4\x58\x3b\xf7\ +\xf6\x34\x90\x88\x39\x04\x1c\x59\x1f\xfe\xdb\xd5\x3c\x5f\x9d\x4b\ +\x32\xfd\x44\xb2\xba\xd7\xfa\xb6\x60\xcf\xde\x16\xdc\x90\x45\x4c\ +\x4a\x2a\x9e\x62\xfe\x4e\xc5\xc8\xc1\x4e\xda\x76\x86\xe8\xe9\x0a\ +\xe3\xd8\x92\x58\xd4\xc6\xb2\x44\x6d\x78\x2a\x53\xe1\xca\x7c\x99\ +\x63\x5d\xbf\x56\x9d\xbd\x9f\x44\x18\x7a\xba\x95\x27\x0f\xb4\xd3\ +\xdc\x18\xc0\xf3\x0d\x52\x40\xd8\xb5\xb0\xa4\x20\x14\xb2\x70\x6c\ +\x81\x63\xcb\xaa\x42\xd6\xfd\xb7\xf4\xec\xa3\x06\xa0\x50\x52\xd8\ +\x4e\x1b\x7e\x4a\xd3\x31\xf9\x29\xcf\xfe\xd4\x49\x7f\x5f\x13\xfb\ +\xfa\x9b\x71\x43\x92\x58\xd4\x21\x18\x90\xac\xde\xb0\x42\x50\x13\ +\x58\x33\xf3\x88\x6b\xa1\xfd\x65\x96\xf2\x79\xc6\x43\x7b\xd8\x75\ +\x38\xcc\x3d\xdd\xd1\xaa\xcf\x71\xe4\xff\x7f\x91\x56\x33\xaf\xea\ +\x37\xe7\xa1\x94\x21\x16\xb5\xd1\x06\x2c\x29\x36\xf5\x72\x9b\x96\ +\x95\xc0\xc4\xda\x9d\x78\x83\x43\x53\x22\x80\x65\x09\x1c\xfb\x86\ +\xc1\x00\xe7\x25\x70\x14\x48\x6f\x1e\x22\x51\xe3\x75\xd9\xb6\xa5\ +\x81\xa3\x32\xb1\xfb\xf4\x0c\x30\xb8\xb1\x82\x9b\xb0\x09\x60\x30\ +\xb1\xfb\xf4\xcc\xbf\xa0\xe9\x6e\xae\x5a\xdf\x4b\x81\x00\x00\x00\ +\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +" + +qt_resource_name = b"\ +\x00\x07\ +\x07\x3b\xe0\xb3\ +\x00\x70\ +\x00\x6c\x00\x75\x00\x67\x00\x69\x00\x6e\x00\x73\ +\x00\x0a\ +\x03\x63\xb1\x44\ +\x00\x68\ +\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x77\x00\x6f\x00\x72\x00\x6c\x00\x64\ +\x00\x08\ +\x0a\x61\x5a\xa7\ +\x00\x69\ +\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ +\x00\x00\x00\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x76\x3b\x1a\xe2\xdb\ +" + +qt_version = [int(v) for v in QtCore.qVersion().split('.')] +if qt_version < [5, 8, 0]: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/helloworld/resources.qrc b/helloworld/resources.qrc new file mode 100644 index 0000000..0d72139 --- /dev/null +++ b/helloworld/resources.qrc @@ -0,0 +1,5 @@ + + + icon.png + + diff --git a/helloworld/scripts/compile-strings.sh b/helloworld/scripts/compile-strings.sh new file mode 100644 index 0000000..9d76083 --- /dev/null +++ b/helloworld/scripts/compile-strings.sh @@ -0,0 +1,12 @@ +#!/bin/bash +LRELEASE=$1 +LOCALES=$2 + + +for LOCALE in ${LOCALES} +do + echo "Processing: ${LOCALE}.ts" + # Note we don't use pylupdate with qt .pro file approach as it is flakey + # about what is made available. + $LRELEASE i18n/${LOCALE}.ts +done diff --git a/helloworld/scripts/run-env-linux.sh b/helloworld/scripts/run-env-linux.sh new file mode 100644 index 0000000..668247c --- /dev/null +++ b/helloworld/scripts/run-env-linux.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +QGIS_PREFIX_PATH=/usr/local/qgis-2.0 +if [ -n "$1" ]; then + QGIS_PREFIX_PATH=$1 +fi + +echo ${QGIS_PREFIX_PATH} + + +export QGIS_PREFIX_PATH=${QGIS_PREFIX_PATH} +export QGIS_PATH=${QGIS_PREFIX_PATH} +export LD_LIBRARY_PATH=${QGIS_PREFIX_PATH}/lib +export PYTHONPATH=${QGIS_PREFIX_PATH}/share/qgis/python:${QGIS_PREFIX_PATH}/share/qgis/python/plugins:${PYTHONPATH} + +echo "QGIS PATH: $QGIS_PREFIX_PATH" +export QGIS_DEBUG=0 +export QGIS_LOG_FILE=/tmp/inasafe/realtime/logs/qgis.log + +export PATH=${QGIS_PREFIX_PATH}/bin:$PATH + +echo "This script is intended to be sourced to set up your shell to" +echo "use a QGIS 2.0 built in $QGIS_PREFIX_PATH" +echo +echo "To use it do:" +echo "source $BASH_SOURCE /your/optional/install/path" +echo +echo "Then use the make file supplied here e.g. make guitest" diff --git a/helloworld/scripts/update-strings.sh b/helloworld/scripts/update-strings.sh new file mode 100644 index 0000000..a31f712 --- /dev/null +++ b/helloworld/scripts/update-strings.sh @@ -0,0 +1,56 @@ +#!/bin/bash +LOCALES=$* + +# Get newest .py files so we don't update strings unnecessarily + +CHANGED_FILES=0 +PYTHON_FILES=`find . -regex ".*\(ui\|py\)$" -type f` +for PYTHON_FILE in $PYTHON_FILES +do + CHANGED=$(stat -c %Y $PYTHON_FILE) + if [ ${CHANGED} -gt ${CHANGED_FILES} ] + then + CHANGED_FILES=${CHANGED} + fi +done + +# Qt translation stuff +# for .ts file +UPDATE=false +for LOCALE in ${LOCALES} +do + TRANSLATION_FILE="i18n/$LOCALE.ts" + if [ ! -f ${TRANSLATION_FILE} ] + then + # Force translation string collection as we have a new language file + touch ${TRANSLATION_FILE} + UPDATE=true + break + fi + + MODIFICATION_TIME=$(stat -c %Y ${TRANSLATION_FILE}) + if [ ${CHANGED_FILES} -gt ${MODIFICATION_TIME} ] + then + # Force translation string collection as a .py file has been updated + UPDATE=true + break + fi +done + +if [ ${UPDATE} == true ] +# retrieve all python files +then + echo ${PYTHON_FILES} + # update .ts + echo "Please provide translations by editing the translation files below:" + for LOCALE in ${LOCALES} + do + echo "i18n/"${LOCALE}".ts" + # Note we don't use pylupdate with qt .pro file approach as it is flakey + # about what is made available. + pylupdate4 -noobsolete ${PYTHON_FILES} -ts i18n/${LOCALE}.ts + done +else + echo "No need to edit any translation files (.ts) because no python files" + echo "has been updated since the last update translation. " +fi diff --git a/helloworld/test/__init__.py b/helloworld/test/__init__.py new file mode 100644 index 0000000..8feeb0b --- /dev/null +++ b/helloworld/test/__init__.py @@ -0,0 +1,2 @@ +# import qgis libs so that ve set the correct sip api version +import qgis # pylint: disable=W0611 # NOQA \ No newline at end of file diff --git a/helloworld/test/qgis_interface.py b/helloworld/test/qgis_interface.py new file mode 100644 index 0000000..a407052 --- /dev/null +++ b/helloworld/test/qgis_interface.py @@ -0,0 +1,205 @@ +# coding=utf-8 +"""QGIS plugin implementation. + +.. note:: 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. + +.. note:: This source code was copied from the 'postgis viewer' application + with original authors: + Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk + Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org + Copyright (c) 2014 Tim Sutton, tim@linfiniti.com + +""" + +__author__ = 'tim@linfiniti.com' +__revision__ = '$Format:%H$' +__date__ = '10/01/2011' +__copyright__ = ( + 'Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and ' + 'Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org' + 'Copyright (c) 2014 Tim Sutton, tim@linfiniti.com' +) + +import logging +from qgis.PyQt.QtCore import QObject, pyqtSlot, pyqtSignal +from qgis.core import QgsMapLayerRegistry +from qgis.gui import QgsMapCanvasLayer +LOGGER = logging.getLogger('QGIS') + + +#noinspection PyMethodMayBeStatic,PyPep8Naming +class QgisInterface(QObject): + """Class to expose QGIS objects and functions to plugins. + + This class is here for enabling us to run unit tests only, + so most methods are simply stubs. + """ + currentLayerChanged = pyqtSignal(QgsMapCanvasLayer) + + def __init__(self, canvas): + """Constructor + :param canvas: + """ + QObject.__init__(self) + self.canvas = canvas + # Set up slots so we can mimic the behaviour of QGIS when layers + # are added. + LOGGER.debug('Initialising canvas...') + # noinspection PyArgumentList + QgsMapLayerRegistry.instance().layersAdded.connect(self.addLayers) + # noinspection PyArgumentList + QgsMapLayerRegistry.instance().layerWasAdded.connect(self.addLayer) + # noinspection PyArgumentList + QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers) + + # For processing module + self.destCrs = None + + @pyqtSlot('QStringList') + def addLayers(self, layers): + """Handle layers being added to the registry so they show up in canvas. + + :param layers: list list of map layers that were added + + .. note:: The QgsInterface api does not include this method, + it is added here as a helper to facilitate testing. + """ + #LOGGER.debug('addLayers called on qgis_interface') + #LOGGER.debug('Number of layers being added: %s' % len(layers)) + #LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers())) + current_layers = self.canvas.layers() + final_layers = [] + for layer in current_layers: + final_layers.append(QgsMapCanvasLayer(layer)) + for layer in layers: + final_layers.append(QgsMapCanvasLayer(layer)) + + self.canvas.setLayerSet(final_layers) + #LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers())) + + @pyqtSlot('QgsMapLayer') + def addLayer(self, layer): + """Handle a layer being added to the registry so it shows up in canvas. + + :param layer: list list of map layers that were added + + .. note: The QgsInterface api does not include this method, it is added + here as a helper to facilitate testing. + + .. note: The addLayer method was deprecated in QGIS 1.8 so you should + not need this method much. + """ + pass + + @pyqtSlot() + def removeAllLayers(self): + """Remove layers from the canvas before they get deleted.""" + self.canvas.setLayerSet([]) + + def newProject(self): + """Create new project.""" + # noinspection PyArgumentList + QgsMapLayerRegistry.instance().removeAllMapLayers() + + # ---------------- API Mock for QgsInterface follows ------------------- + + def zoomFull(self): + """Zoom to the map full extent.""" + pass + + def zoomToPrevious(self): + """Zoom to previous view extent.""" + pass + + def zoomToNext(self): + """Zoom to next view extent.""" + pass + + def zoomToActiveLayer(self): + """Zoom to extent of active layer.""" + pass + + def addVectorLayer(self, path, base_name, provider_key): + """Add a vector layer. + + :param path: Path to layer. + :type path: str + + :param base_name: Base name for layer. + :type base_name: str + + :param provider_key: Provider key e.g. 'ogr' + :type provider_key: str + """ + pass + + def addRasterLayer(self, path, base_name): + """Add a raster layer given a raster layer file name + + :param path: Path to layer. + :type path: str + + :param base_name: Base name for layer. + :type base_name: str + """ + pass + + def activeLayer(self): + """Get pointer to the active layer (layer selected in the legend).""" + # noinspection PyArgumentList + layers = QgsMapLayerRegistry.instance().mapLayers() + for item in layers: + return layers[item] + + def addToolBarIcon(self, action): + """Add an icon to the plugins toolbar. + + :param action: Action to add to the toolbar. + :type action: QAction + """ + pass + + def removeToolBarIcon(self, action): + """Remove an action (icon) from the plugin toolbar. + + :param action: Action to add to the toolbar. + :type action: QAction + """ + pass + + def addToolBar(self, name): + """Add toolbar with specified name. + + :param name: Name for the toolbar. + :type name: str + """ + pass + + def mapCanvas(self): + """Return a pointer to the map canvas.""" + return self.canvas + + def mainWindow(self): + """Return a pointer to the main window. + + In case of QGIS it returns an instance of QgisApp. + """ + pass + + def addDockWidget(self, area, dock_widget): + """Add a dock widget to the main window. + + :param area: Where in the ui the dock should be placed. + :type area: + + :param dock_widget: A dock widget to add to the UI. + :type dock_widget: QDockWidget + """ + pass + + def legendInterface(self): + """Get the legend.""" + return self.canvas diff --git a/helloworld/test/tenbytenraster.asc b/helloworld/test/tenbytenraster.asc new file mode 100644 index 0000000..96a0ee1 --- /dev/null +++ b/helloworld/test/tenbytenraster.asc @@ -0,0 +1,19 @@ +NCOLS 10 +NROWS 10 +XLLCENTER 1535380.000000 +YLLCENTER 5083260.000000 +DX 10 +DY 10 +NODATA_VALUE -9999 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 +CRS +NOTES diff --git a/helloworld/test/tenbytenraster.asc.aux.xml b/helloworld/test/tenbytenraster.asc.aux.xml new file mode 100644 index 0000000..cfb1578 --- /dev/null +++ b/helloworld/test/tenbytenraster.asc.aux.xml @@ -0,0 +1,13 @@ + + + Point + + + + 9 + 4.5 + 0 + 2.872281323269 + + + diff --git a/helloworld/test/tenbytenraster.keywords b/helloworld/test/tenbytenraster.keywords new file mode 100644 index 0000000..8be3f61 --- /dev/null +++ b/helloworld/test/tenbytenraster.keywords @@ -0,0 +1 @@ +title: Tenbytenraster diff --git a/helloworld/test/tenbytenraster.lic b/helloworld/test/tenbytenraster.lic new file mode 100644 index 0000000..8345533 --- /dev/null +++ b/helloworld/test/tenbytenraster.lic @@ -0,0 +1,18 @@ + + + + Tim Sutton, Linfiniti Consulting CC + + + + tenbytenraster.asc + 2700044251 + Yes + Tim Sutton + Tim Sutton (QGIS Source Tree) + Tim Sutton + This data is publicly available from QGIS Source Tree. The original + file was created and contributed to QGIS by Tim Sutton. + + + diff --git a/helloworld/test/tenbytenraster.prj b/helloworld/test/tenbytenraster.prj new file mode 100644 index 0000000..a30c00a --- /dev/null +++ b/helloworld/test/tenbytenraster.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/helloworld/test/tenbytenraster.qml b/helloworld/test/tenbytenraster.qml new file mode 100644 index 0000000..85247d4 --- /dev/null +++ b/helloworld/test/tenbytenraster.qml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 0 + diff --git a/helloworld/test/test_helloworld_dialog.py b/helloworld/test/test_helloworld_dialog.py new file mode 100644 index 0000000..924cd5d --- /dev/null +++ b/helloworld/test/test_helloworld_dialog.py @@ -0,0 +1,55 @@ +# coding=utf-8 +"""Dialog test. + +.. note:: 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. + +""" + +__author__ = 'fintanosullivan14@gmail.com' +__date__ = '2020-12-07' +__copyright__ = 'Copyright 2020, Fintan O'Sullivan' + +import unittest + +from qgis.PyQt.QtGui import QDialogButtonBox, QDialog + +from helloworld_dialog import helloWorldDialog + +from utilities import get_qgis_app +QGIS_APP = get_qgis_app() + + +class helloWorldDialogTest(unittest.TestCase): + """Test dialog works.""" + + def setUp(self): + """Runs before each test.""" + self.dialog = helloWorldDialog(None) + + def tearDown(self): + """Runs after each test.""" + self.dialog = None + + def test_dialog_ok(self): + """Test we can click OK.""" + + button = self.dialog.button_box.button(QDialogButtonBox.Ok) + button.click() + result = self.dialog.result() + self.assertEqual(result, QDialog.Accepted) + + def test_dialog_cancel(self): + """Test we can click cancel.""" + button = self.dialog.button_box.button(QDialogButtonBox.Cancel) + button.click() + result = self.dialog.result() + self.assertEqual(result, QDialog.Rejected) + +if __name__ == "__main__": + suite = unittest.makeSuite(helloWorldDialogTest) + runner = unittest.TextTestRunner(verbosity=2) + runner.run(suite) + diff --git a/helloworld/test/test_init.py b/helloworld/test/test_init.py new file mode 100644 index 0000000..a11ca44 --- /dev/null +++ b/helloworld/test/test_init.py @@ -0,0 +1,64 @@ +# coding=utf-8 +"""Tests QGIS plugin init.""" + +__author__ = 'Tim Sutton ' +__revision__ = '$Format:%H$' +__date__ = '17/10/2010' +__license__ = "GPL" +__copyright__ = 'Copyright 2012, Australia Indonesia Facility for ' +__copyright__ += 'Disaster Reduction' + +import os +import unittest +import logging +import configparser + +LOGGER = logging.getLogger('QGIS') + + +class TestInit(unittest.TestCase): + """Test that the plugin init is usable for QGIS. + + Based heavily on the validator class by Alessandro + Passoti available here: + + http://github.com/qgis/qgis-django/blob/master/qgis-app/ + plugins/validator.py + + """ + + def test_read_init(self): + """Test that the plugin __init__ will validate on plugins.qgis.org.""" + + # You should update this list according to the latest in + # https://github.com/qgis/qgis-django/blob/master/qgis-app/ + # plugins/validator.py + + required_metadata = [ + 'name', + 'description', + 'version', + 'qgisMinimumVersion', + 'email', + 'author'] + + file_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), os.pardir, + 'metadata.txt')) + LOGGER.info(file_path) + metadata = [] + parser = configparser.ConfigParser() + parser.optionxform = str + parser.read(file_path) + message = 'Cannot find a section named "general" in %s' % file_path + assert parser.has_section('general'), message + metadata.extend(parser.items('general')) + + for expectation in required_metadata: + message = ('Cannot find metadata "%s" in metadata source (%s).' % ( + expectation, file_path)) + + self.assertIn(expectation, dict(metadata), message) + +if __name__ == '__main__': + unittest.main() diff --git a/helloworld/test/test_qgis_environment.py b/helloworld/test/test_qgis_environment.py new file mode 100644 index 0000000..1becb30 --- /dev/null +++ b/helloworld/test/test_qgis_environment.py @@ -0,0 +1,60 @@ +# coding=utf-8 +"""Tests for QGIS functionality. + + +.. note:: 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. + +""" +__author__ = 'tim@linfiniti.com' +__date__ = '20/01/2011' +__copyright__ = ('Copyright 2012, Australia Indonesia Facility for ' + 'Disaster Reduction') + +import os +import unittest +from qgis.core import ( + QgsProviderRegistry, + QgsCoordinateReferenceSystem, + QgsRasterLayer) + +from .utilities import get_qgis_app +QGIS_APP = get_qgis_app() + + +class QGISTest(unittest.TestCase): + """Test the QGIS Environment""" + + def test_qgis_environment(self): + """QGIS environment has the expected providers""" + + r = QgsProviderRegistry.instance() + self.assertIn('gdal', r.providerList()) + self.assertIn('ogr', r.providerList()) + self.assertIn('postgres', r.providerList()) + + def test_projection(self): + """Test that QGIS properly parses a wkt string. + """ + crs = QgsCoordinateReferenceSystem() + wkt = ( + 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",' + 'SPHEROID["WGS_1984",6378137.0,298.257223563]],' + 'PRIMEM["Greenwich",0.0],UNIT["Degree",' + '0.0174532925199433]]') + crs.createFromWkt(wkt) + auth_id = crs.authid() + expected_auth_id = 'EPSG:4326' + self.assertEqual(auth_id, expected_auth_id) + + # now test for a loaded layer + path = os.path.join(os.path.dirname(__file__), 'tenbytenraster.asc') + title = 'TestRaster' + layer = QgsRasterLayer(path, title) + auth_id = layer.crs().authid() + self.assertEqual(auth_id, expected_auth_id) + +if __name__ == '__main__': + unittest.main() diff --git a/helloworld/test/test_resources.py b/helloworld/test/test_resources.py new file mode 100644 index 0000000..8b1df4c --- /dev/null +++ b/helloworld/test/test_resources.py @@ -0,0 +1,44 @@ +# coding=utf-8 +"""Resources test. + +.. note:: 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. + +""" + +__author__ = 'fintanosullivan14@gmail.com' +__date__ = '2020-12-07' +__copyright__ = 'Copyright 2020, Fintan O'Sullivan' + +import unittest + +from qgis.PyQt.QtGui import QIcon + + + +class helloWorldDialogTest(unittest.TestCase): + """Test rerources work.""" + + def setUp(self): + """Runs before each test.""" + pass + + def tearDown(self): + """Runs after each test.""" + pass + + def test_icon_png(self): + """Test we can click OK.""" + path = ':/plugins/helloWorld/icon.png' + icon = QIcon(path) + self.assertFalse(icon.isNull()) + +if __name__ == "__main__": + suite = unittest.makeSuite(helloWorldResourcesTest) + runner = unittest.TextTestRunner(verbosity=2) + runner.run(suite) + + + diff --git a/helloworld/test/test_translations.py b/helloworld/test/test_translations.py new file mode 100644 index 0000000..035dc62 --- /dev/null +++ b/helloworld/test/test_translations.py @@ -0,0 +1,55 @@ +# coding=utf-8 +"""Safe Translations Test. + +.. note:: 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. + +""" +from .utilities import get_qgis_app + +__author__ = 'ismailsunni@yahoo.co.id' +__date__ = '12/10/2011' +__copyright__ = ('Copyright 2012, Australia Indonesia Facility for ' + 'Disaster Reduction') +import unittest +import os + +from qgis.PyQt.QtCore import QCoreApplication, QTranslator + +QGIS_APP = get_qgis_app() + + +class SafeTranslationsTest(unittest.TestCase): + """Test translations work.""" + + def setUp(self): + """Runs before each test.""" + if 'LANG' in iter(os.environ.keys()): + os.environ.__delitem__('LANG') + + def tearDown(self): + """Runs after each test.""" + if 'LANG' in iter(os.environ.keys()): + os.environ.__delitem__('LANG') + + def test_qgis_translations(self): + """Test that translations work.""" + parent_path = os.path.join(__file__, os.path.pardir, os.path.pardir) + dir_path = os.path.abspath(parent_path) + file_path = os.path.join( + dir_path, 'i18n', 'af.qm') + translator = QTranslator() + translator.load(file_path) + QCoreApplication.installTranslator(translator) + + expected_message = 'Goeie more' + real_message = QCoreApplication.translate("@default", 'Good morning') + self.assertEqual(real_message, expected_message) + + +if __name__ == "__main__": + suite = unittest.makeSuite(SafeTranslationsTest) + runner = unittest.TextTestRunner(verbosity=2) + runner.run(suite) diff --git a/helloworld/test/utilities.py b/helloworld/test/utilities.py new file mode 100644 index 0000000..be7ee3b --- /dev/null +++ b/helloworld/test/utilities.py @@ -0,0 +1,61 @@ +# coding=utf-8 +"""Common functionality used by regression tests.""" + +import sys +import logging + + +LOGGER = logging.getLogger('QGIS') +QGIS_APP = None # Static variable used to hold hand to running QGIS app +CANVAS = None +PARENT = None +IFACE = None + + +def get_qgis_app(): + """ Start one QGIS application to test against. + + :returns: Handle to QGIS app, canvas, iface and parent. If there are any + errors the tuple members will be returned as None. + :rtype: (QgsApplication, CANVAS, IFACE, PARENT) + + If QGIS is already running the handle to that app will be returned. + """ + + try: + from qgis.PyQt import QtGui, QtCore + from qgis.core import QgsApplication + from qgis.gui import QgsMapCanvas + from .qgis_interface import QgisInterface + except ImportError: + return None, None, None, None + + global QGIS_APP # pylint: disable=W0603 + + if QGIS_APP is None: + gui_flag = True # All test will run qgis in gui mode + #noinspection PyPep8Naming + QGIS_APP = QgsApplication(sys.argv, gui_flag) + # Make sure QGIS_PREFIX_PATH is set in your env if needed! + QGIS_APP.initQgis() + s = QGIS_APP.showSettings() + LOGGER.debug(s) + + global PARENT # pylint: disable=W0603 + if PARENT is None: + #noinspection PyPep8Naming + PARENT = QtGui.QWidget() + + global CANVAS # pylint: disable=W0603 + if CANVAS is None: + #noinspection PyPep8Naming + CANVAS = QgsMapCanvas(PARENT) + CANVAS.resize(QtCore.QSize(400, 400)) + + global IFACE # pylint: disable=W0603 + if IFACE is None: + # QgisInterface is a stub implementation of the QGIS plugin interface + #noinspection PyPep8Naming + IFACE = QgisInterface(CANVAS) + + return QGIS_APP, CANVAS, IFACE, PARENT