Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions plugin_exporter/metadata.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ name=Plugin Exporter
qgisMinimumVersion=3.20
qgisMaximumVersion=4.99
description=A QGIS plugin for exporting plugins
version=0.2.2
version=0.3.0
author=Francis Lapointe
email=francis.lapointe5@usherbrooke.ca

about=Plugin Exporter is a QGIS plugin that can export installed plugins into a .csv or .json file. The user can export all the installed plugins or select the plugins they want to export. Plugin Exporter can also use the generated file to install the plugins back in QGIS. Third party repositories are also supported as of v0.2.0.
about=Plugin Exporter is a QGIS plugin that can export installed plugins into a .csv, .json, .md or .pdf file. The user can export all the installed plugins or select the plugins they want to export. Plugin Exporter can also use a .csv or .json file to install the plugins back in QGIS. Third party repositories are also supported as of v0.2.0.

tracker=https://github.com/Scriptbash/PluginExporter/issues
repository=https://github.com/Scriptbash/PluginExporter
Expand All @@ -24,6 +24,8 @@ supportsQt6=True
hasProcessingProvider=no
# Uncomment the following line and add your changelog:
changelog=
v0.3.0
- Add Markdown (.md) and PDF (.pdf) export formats (export only)
v0.2.2
- Migrate to pyQt6
v0.2.1
Expand Down
122 changes: 119 additions & 3 deletions plugin_exporter/plugin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"""

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtGui import QIcon, QTextDocument
from qgis.PyQt.QtPrintSupport import QPrinter
from qgis.PyQt.QtWidgets import QAction, QLabel, QCheckBox
from qgis.core import Qgis, QgsSettings
from pyplugin_installer.installer_data import repositories
Expand All @@ -32,7 +33,9 @@
import os.path
import csv
import json
import html
import pathlib
from datetime import datetime

# Initialize Qt resources from file resources.py
from .resources import *
Expand Down Expand Up @@ -371,6 +374,22 @@ def export_plugins(self):
self.iface.messageBar().pushSuccess(
"Success", "Selected plugins were exported successfully."
)
elif file_format == ".md":
with open(output_file, "w", encoding="utf8") as file:
file.write(self.build_markdown(plugin_list, repos))
self.iface.messageBar().pushSuccess(
"Success", "Selected plugins were exported successfully."
)
elif file_format == ".pdf":
document = QTextDocument()
document.setHtml(self.build_html(plugin_list, repos))
printer = QPrinter()
printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat)
printer.setOutputFileName(output_file)
document.print(printer)
self.iface.messageBar().pushSuccess(
"Success", "Selected plugins were exported successfully."
)
except IsADirectoryError:
self.iface.messageBar().pushMessage(
"Error",
Expand Down Expand Up @@ -485,6 +504,98 @@ def add_repository(self, repo_info):
"Success", repo_name + " was added to the repositories."
)

# Collects the third party repositories and every metadata field of each
# selected plugin. Used by the human readable exports (.md and .pdf), which
# are export only: they cannot be imported back. Each plugin keeps all of
# the fields the plugin manager exposes for it (same data as the .json
# export), not just a fixed subset.
def build_report_data(self, plugin_list, repos):
repo_rows = []
if repos:
for name, value in repos.items():
if name == "QGIS Official Plugin Repository":
continue
repo_rows.append([name, value["url"]])

plugins = []
for plugin in plugin_list:
# Preserve the metadata key order; fall back to the id for the title
title = plugin.get("name") or plugin.get("id") or "Unknown plugin"
fields = [(str(k), "" if v is None else str(v)) for k, v in plugin.items()]
plugins.append((title, fields))
return repo_rows, plugins

# Builds a Markdown document listing the selected plugins
def build_markdown(self, plugin_list, repos):
repo_rows, plugins = self.build_report_data(plugin_list, repos)
generated = datetime.now().strftime("%Y-%m-%d %H:%M")

def cell(text):
return str(text).replace("|", "\\|").replace("\n", " ")

lines = [
"# QGIS Plugins Export",
"",
"_Generated by Plugin Exporter on " + generated + "_",
"",
]
if repo_rows:
lines += ["## Third party repositories", "", "| Name | URL |", "| --- | --- |"]
for row in repo_rows:
lines.append("| " + " | ".join(cell(c) for c in row) + " |")
lines.append("")
lines += ["## Plugins", ""]
for title, fields in plugins:
lines += [
"### " + cell(title),
"",
"| Field | Value |",
"| --- | --- |",
]
for key, value in fields:
lines.append("| " + cell(key) + " | " + cell(value) + " |")
lines.append("")
return "\n".join(lines)

# Builds an HTML document used to render the PDF export
def build_html(self, plugin_list, repos):
repo_rows, plugins = self.build_report_data(plugin_list, repos)
generated = datetime.now().strftime("%Y-%m-%d %H:%M")

def esc(text):
return html.escape(str(text)).replace("\n", "<br>")

def table(headers, rows):
html_rows = [
"<tr>"
+ "".join("<th>" + html.escape(h) + "</th>" for h in headers)
+ "</tr>"
]
for row in rows:
html_rows.append(
"<tr>" + "".join("<td>" + esc(c) + "</td>" for c in row) + "</tr>"
)
return (
'<table border="1" cellspacing="0" cellpadding="4" width="100%">'
+ "".join(html_rows)
+ "</table>"
)

parts = [
"<html><body>",
"<h1>QGIS Plugins Export</h1>",
"<p><i>Generated by Plugin Exporter on " + html.escape(generated) + "</i></p>",
]
if repo_rows:
parts.append("<h2>Third party repositories</h2>")
parts.append(table(["Name", "URL"], repo_rows))
parts.append("<h2>Plugins</h2>")
for title, fields in plugins:
parts.append("<h3>" + esc(title) + "</h3>")
parts.append(table(["Field", "Value"], fields))
parts.append("</body></html>")
return "".join(parts)

# Disables and enables widgets
def toggle_widget(self):
if self.dlg.rd_import.isChecked():
Expand All @@ -502,7 +613,12 @@ def toggle_widget(self):

# Sets the file extension filter for the QgsFileWidget
def set_filter(self):
if self.dlg.combo_file_format.currentText() == ".json":
current_format = self.dlg.combo_file_format.currentText()
if current_format == ".json":
self.dlg.file_output_export.setFilter("*.json")
elif self.dlg.combo_file_format.currentText() == ".csv":
elif current_format == ".csv":
self.dlg.file_output_export.setFilter("*.csv")
elif current_format == ".md":
self.dlg.file_output_export.setFilter("*.md")
elif current_format == ".pdf":
self.dlg.file_output_export.setFilter("*.pdf")
10 changes: 10 additions & 0 deletions plugin_exporter/plugin_exporter_dialog_base.ui
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@
<string>.json</string>
</property>
</item>
<item>
<property name="text">
<string>.md</string>
</property>
</item>
<item>
<property name="text">
<string>.pdf</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
Expand Down